Java 经典面试题:聊一聊 JUC 下的 LinkedBlockingQueue

Java 经典面试题:聊一聊 JUC 下的 LinkedBlockingQueue

本文聊一下 JUC 下的 LinkedBlockingQueue 行列,先说说 LinkedBlockingQueue 行列的特色,然后再从源码的视点聊一聊 LinkedBlockingQueue 的首要完成~

LinkedBlockingQueue 有以下特色:

LinkedBlockingQueue 是堵塞行列,底层是单链表完成的~
元素从行列尾进队,从行列头出队,契合FIFO~
能够运用 Collection 和 Iterator 两个接口的一切操作,由于完成了两者的接口~
LinkedBlockingQueue 行列读写操作都加了锁,可是读写用的是两把不同的锁,所以能够一起读写操作~
LinkedBlockingQueue 行列承继了 AbstractQueue 类,完成了 BlockingQueue 接口,LinkedBlockingQueue 首要有以下接口:

//将指定的元素刺进到此行列的尾部(假如当即可行且不会超越该行列的容量)
//在成功时回来 true,假如此行列已满,则抛IllegalStateException。
boolean add(E e);

//将指定的元素刺进到此行列的尾部(假如当即可行且不会超越该行列的容量)
// 将指定的元素刺进此行列的尾部,假如该行列已满,
//则在抵达指定的等候时刻之前等候可用的空间,该办法可中止
boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;

//将指定的元素刺进此行列的尾部,假如该行列已满,则一向比及(堵塞)。
void put(E e) throws InterruptedException;

//获取并移除此行列的头部,假如没有元素则等候(堵塞),
//直到有元素将唤醒等候线程履行该操作
E take() throws InterruptedException;

//获取并移除此行列的头,假如此行列为空,则回来 null。
E poll();
//获取并移除此行列的头部,在指定的等候时刻前一向比及获取元素, //超越时刻办法将完毕
E poll(long timeout, TimeUnit unit) throws InterruptedException;

//从此行列中移除指定元素的单个实例(假如存在)。
boolean remove(Object o);

//获取但不移除此行列的头元素,没有则跑反常NoSuchElementException
E element();

//获取但不移除此行列的头;假如此行列为空,则回来 null。
E peek();
LinkedBlockingQueue 行列的读写办法十分的多,可是常用的是 put()、take()办法,由于它们两是堵塞的,所以咱们就从源码的视点来聊一聊 LinkedBlockingQueue 行列中这两个办法的完成。

先来看看 put()办法,源码如下:

public void put(E e) throws InterruptedException {

if (e == null) throw new NullPointerException();
// 预先设置 c 的值为 -1,表明失利
int c = -1;
Node<E> node = new Node<E>(e);
// 获取写锁
final ReentrantLock putLock = this.putLock;
// 获取当时行列的巨细
final AtomicInteger count = this.count;
// 设置可中止锁
putLock.lockInterruptibly();
try {
// 行列满了
// 当时线程堵塞,等候其他线程的唤醒(其他线程 take 成功后就会唤醒此处线程)
while (count.get() == capacity) {
// 无限期等候
notFull.await();
}
// 新增到行列尾部
enqueue(node);
// 获取当时的行列数
c = count.getAndIncrement();
// 假如行列未满,测验唤醒一个put的等候线程
if (c + 1 < capacity)
notFull.signal();
} finally {
// 开释锁
putLock.unlock();
}
if (c == 0)
signalNotEmpty();

}
put()办法的源码并不难,十分简略就看懂,put()办法的进程大约如下:

1、先加锁,确保容器的并发安全~
2、行列新增数据,将数据追加到行列尾部~
3、新增时,假如行列满了,当时线程是会被堵塞的,等候被唤醒~
4、新增数据成功后,在恰当机遇,会引发 put 的等候线程(行列不满时),或许 take 的等候线程(行列不为空时),这样确保行列一旦满意 put 或许 take 条件时,立马就能引发堵塞线程,持续运转,确保了引发的机遇不被糟蹋offer 就有两两种,一种是直接回来 false,另一种是超越必定时刻后回来 false~
5、开释锁~
其他的新增办法,例如 offer,能够检查源码,跟put() 办法迥然不同,相差不大~

再来看看 take()办法,源码如下:

public E take() throws InterruptedException {

E x;
// 默许负数
int c = -1;
// 当时链表的个数
final AtomicInteger count = this.count;
//获取读锁
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
// 当行列为空时,堵塞,等候其他线程唤醒
while (count.get() == 0) {
notEmpty.await();
}
// 从行列的头部拿出一个元素
x = dequeue();
//减一操作,C比实在的行列数据大一
c = count.getAndDecrement();
// c 大于 0 ,表明行列有值,能够唤醒之前被堵塞的读线程
if (c > 1)
notEmpty.signal();
} finally {
// 开释锁
takeLock.unlock();
}
// 行列未满,能够唤醒 put 等候线程~
if (c == capacity)
signalNotFull();
return x;

}
take()办法跟 put() 办法相似,是一个相反的操作,我就不做过多的说明晰~

以上便是 LinkedBlockingQueue 行列的简略源码解析,期望对你的面试或许作业有所协助,感谢你的阅览~

原文地址https://www.cnblogs.com/jamaler/p/12849927.html