孤独,所有人都是孤独的,没有人能独自超脱这一切。 ——玛娅·安杰格
LinkedBlockingQueue - 单链表实现的阻塞队列。该队列按 FIFO(先进先出)排序元素,新元素从队列尾部插入,从队首获取元素.是深入并发编程的基础数据结构.
remove 方法,BlockingQueue 类注释中定义的是抛异常,但 LinkedBlockingQueue 中 remove 方法实际是返回 false。
锁有 take 锁和 put 锁,是为了保证队列操作时的线程安全,设计两种锁,是为了 take 和 put 两种操作可以同时进行,而互不影响。
以 offer 方法为例.
将元素E添加到队列的末尾.
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
// 如果“队列已满”,则返回false,表示插入失败。
final AtomicInteger count = this.count;
if (count.get() == capacity) return false;
int c = -1;
// 新建“节点e”
Node<E> node = new Node(e);
final ReentrantLock putLock = this.putLock;
// 获取“插入锁putLock”
putLock.lock();
try {
// 再次对“队列是不是满”的进行判断。
// 若“队列未满”,则插入节点。
if (count.get() < capacity) {
// 插入节点 enqueue(node);
// 将“当前节点数量”+1,并返回“原始的数量”
c = count.getAndIncrement();
// 如果在插入元素之后,队列仍然未满,则唤醒notFull上的等待线程。
if (c + 1 < capacity) notFull.signal();
}
} finally {
// 释放“插入锁putLock”
putLock.unlock();
}
// 如果在插入节点前,队列为空;则插入节点后,唤醒notEmpty上的等待线程
if (c == 0)
signalNotEmpty();
return c >= 0;
}
新增数据成功后,在适当时机,会唤起 put 的等待线程(队列不满时),或者 take 的等待线程(队列不为空时),这样保证队列一旦满足 put 或者 take 条件时,立马就能唤起阻塞线程,继续运行,保证了唤起的时机不被浪费。
以take()为例.取出并返回队列的头。若队列为空,则一直等待。
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
// 获取“取出锁”,若当前线程是中断状态,则抛出InterruptedException异常
takeLock.lockInterruptibly();
try {
// 若“队列为空”,则一直等待。
while (count.get() == 0) {
notEmpty.await();
}
// 取出元素
x = dequeue();
// 取出元素之后,将“节点数量”-1;并返回“原始的节点数量”。
c = count.getAndDecrement();
if (c > 1) notEmpty.signal();
} finally {
// 释放“取出锁”
takeLock.unlock();
}
// 如果在“取出元素之前”,队列是满的;则在取出元素之后,唤醒notFull上的等待线程。
if (c == capacity)
signalNotFull();
return x;
}
理解好阻塞队列很重要,经常会被用在线程池的场景中.
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。