简介
LinkedBlockingQueue是采用单链表实现的有界阻塞安全队列 。
主要属性
// 内部节点,用来存储队列元素
static class Node<E> {
// 节点元素
E item;
// 指向下一个节点
Node<E> next;
Node(E x) { item = x; }
}
/** 队列容量 */
private final int capacity;
/** 当前队列的元素个数 */
private final AtomicInteger count = new AtomicInteger();
/** 队列头部 */
transient Node<E> head;
/** 队列的尾部 */
private transient Node<E> last;
/** 获取元素锁,使用的是非公平锁 */
private final ReentrantLock takeLock = new ReentrantLock();
// 获取元素时,若队列为空,则将会阻塞当前线程并将当前线程加入到notEmpty的等待队列
/** 当队列为空时的等待条件 */
private final Condition notEmpty = takeLock.newCondition();
/** 插入元素锁,使用的是非公平锁 */
private final ReentrantLock putLock = new ReentrantLock();
// 插入元素时,若队列已满,则将会阻塞当前线程并将当前线程加入到notFull的等待队列
/** 当队列满了的等待条件 */
private final Condition notFull = putLock.newCondition();
// 构造函数
public LinkedBlockingQueue() {
// 默认队列大小为Integer.MAX_VALUE
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
入队列
// 入队列,如果队列已满,将会一直阻塞
public void put(E e) throws InterruptedException {
// 不能传入空元素
if (e == null) throw new NullPointerException();
// Note: convention in all put/take/etc is to preset local var
// holding count negative to indicate failure unless set.
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
final AtomicInteger count = this.count;
putLock.lockInterruptibly();
try {
// 如果队列已经满了,就阻塞当前线程,将 当前线程加入到notFull的等待队列中,等待被唤醒
while (count.get() == capacity) {
notFull.await();
}
// 入队列
enqueue(node);
// 队列个数加1,注意,这个返回的值并不是加1后的值
c = count.getAndIncrement();
// 如果当前元素的个数小于队列的容量,就再唤醒一个阻塞在notFull条件上的线程
if (c + 1 < capacity)
notFull.signal();
} finally {
// 释放锁
putLock.unlock();
}
// 如果原队列长度为0,现在添加一个元素了,那么队列就不为空了
// 此时需要唤醒一个阻塞在notEmpty等待队列的线程。
if (c == 0)
signalNotEmpty();
}
// 入队列的方法
private void enqueue(Node<E> node) {
last = last.next = node;
}
// 唤醒一个阻塞在notEmpty等待队列的线程。
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
出队列
// 出队列,如队列为空,则阻塞当前线程直到队列不为空
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
// 如队列为空,则阻塞当前线程,将当前线程加入到notEmpty等待队列,等待被唤醒
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
// 队列元素减1,注意,这个返回值并不是减1后的值
c = count.getAndDecrement();
// 如果出队列之前元素的个数大于1,现在只是出队了一个元素,那么队列还是不为空的,则唤醒notEmpty等待队列的线程
if (c > 1)
notEmpty.signal();
} finally {
// 释放锁
takeLock.unlock();
}
// 如果出队列之前队列是满的,此时出队了一个元素,队列就不满了,此时需要唤醒一个notFull等待队列上的线程
if (c == capacity)
signalNotFull();
return x;
}
// 出队列
private E dequeue() {
// head本身不存储东西
// 这里是把head的下一个节点作为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;
}
// 唤醒一个 notFull 等待队列的线程
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
总结
- LinkedBlockingQueue采用单链表的形式存储元素。
- LinkedBlockingQueue入队与出队采用两把不同的锁,相比ArrayBlockingQueue,LinkedBlockingQueue效率更高。
- LinkedBlockingQueue的默认大小为Integer.MAX_VALUE。
- LinkedBlockingQueue采用的是非公平锁,而ArrayBlockingQueue可以选择公平锁与非公平锁。