LinkedBlockingQueue 是 JUC 中常用的 FIFO 阻塞队列,本系列中我们在了解线程池的原理时,其中 newSingleThreadExecutor 和 newFixedThreadPool 方法中默认的任务队列就是 LinkedBlockingQueue。此外,我们可以使用此队列实现在同一进程中对生产者和消费者的异步化。
1. offer()
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
// 不允许元素为空
if (e == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
int c = -1;
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
// 元素插入前,先获得插入锁
putLock.lockInterruptibly();
try {
// 元素中的数量到达容量阈值,阻塞
while (count.get() == capacity) {
if (nanos <= 0)
return false;// 超时返回失败
nanos = notFull.awaitNanos(nanos);
}
// 插入节点
enqueue(new Node<E>(e));
// 计数,getAndIncrement() 返回的是原子自增前的值
c = count.getAndIncrement();
// 当前至少还能容纳至少一个元素,唤醒一个阻塞在插入操作上的线程
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
// 当前元素插入前,队列为空,唤醒一个阻塞在获取操作上的线程
if (c == 0)
signalNotEmpty();
return true;
}
2. poll()
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
// 取元素前,先要获得 take 锁
takeLock.lockInterruptibly();
try {
// 队列为空,阻塞
while (count.get() == 0) {
if (nanos <= 0)
return null;// 超时返回 null
nanos = notEmpty.awaitNanos(nanos);
}
// 取出队列中的第一个节点的值
x = dequeue();
c = count.getAndDecrement();
// first 元素取出后队列仍不空,唤醒一个阻塞在获取操作上的线程
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
// 如果 first 元素被取出前,队列已满,取出后唤醒一个阻塞在插入操作上的线程
if (c == capacity)
signalNotFull();
return x;
}
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
// head 节点的 item 一直为空,因此这里取 head 的下一个节点的值
Node<E> h = head;
Node<E> first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}