源码解析
LinkedBlockingQueue底层是由链表实现的“有界”队列。构造时如果不指定容量,则最大容量为Integer.MAX_VALUE。LinkedBlockingQueue底层使用了putLock和takeLock分别控制入队和出队操作,提高了吞吐量。
底层结构
/**
* 链表内部结点
*/
static class Node<E> {
E item;
/**
* 代表以下含义中的一个
* - 真实的后继结点
* - 该结点本身,表示后继结点是head.next
* - null,表示该结点为最后一个结点
*/
Node<E> next;
Node(E x) {
item = x;
}
}
/**
* 容量限制,如果没有特定的限制,则为Integer.MAX_VALUE
*/
private final int capacity;
/**
* 队列中元素个数
*/
private final AtomicInteger count = new AtomicInteger();
/**
* 这个头结点只是为了方便删除等操作,减少判断。是一个“伪”头
* 结点。head.next才是真正意义上的队列头。
*/
transient Node<E> head;
/**
* 队列的尾结点
*/
private transient Node<E> last;
/**
* take, poll等方法使用的锁
*/
private final ReentrantLock takeLock = new ReentrantLock();
/**
* take的等待条件(当take时如果队列为空则会在此条件上等待)
*/
private final Condition notEmpty = takeLock.newCondition();
/**
* put, offer等方法使用的锁
*/
private final ReentrantLock putLock = new ReentrantLock();
/**
* put的等待条件(当put时如果队列已满则会在此条件上等待)
*/
private final Condition notFull = putLock.newCondition();
public int size() {
return count.get();
}
/**
* 返回剩余容量,注意这里没有加锁,是非原子操作。所以该方法不能用来判断是否有
* 剩余容量。是非线程安全的
*/
public int remainingCapacity() {
return capacity - count.get();
}
内部工具方法
/**
* 释放notEmpty条件,唤醒在此条件等待的线程
*/
private void signalNotEmpty() {
final ReentrantLock takeLock = this.takeLock;
// 因为notEmpty条件是使用takeLock创建的
// 所以释放条件时,必须先持有锁
takeLock.lock();
try {
notEmpty.signal();
} finally {
takeLock.unlock();
}
}
/**
* 释放notFull条件,唤醒在此条件等待的线程
*/
private void signalNotFull() {
final ReentrantLock putLock = this.putLock;
// 因为notFull条件是使用putLock创建的
// 所以释放条件时,必须先持有锁
putLock.lock();
try {
notFull.signal();
} finally {
putLock.unlock();
}
}
/**
* 入队(插入到链表尾)
*/
private void enqueue(Node<E> node) {
// assert putLock.isHeldByCurrentThread();
// assert last.next == null;
last = last.next = node;
}
/**
* 出队(移除链表头结点)
*/
private E dequeue() {
// assert takeLock.isHeldByCurrentThread();
// assert head.item == null;
Node<E> h = head;
// head结点只是一个“伪”头结点,
// 所以head.next才是真正的头结点,
// 要移除的就是head.next
Node<E> first = h.next;
// 指向自己,帮助GC。并没有直接删除head,因为Itr为
// 弱迭代器。可能仍然有迭代器指向head
h.next = h;
// head后移
head = first;
E x = first.item;
first.item = null;
return x;
}
/**
* Locks to prevent both puts and takes.
*/
void fullyLock() {
putLock.lock();
takeLock.lock();
}
/**
* Unlocks to allow both puts and takes.
*/
void fullyUnlock() {
takeLock.unlock();
putLock.unlock();
}
/**
* 取消内部节点p与前驱trail的链接
*/
void unlink(Node<E> p, Node<E> trail) {
// assert isFullyLocked();
p.item = null;
trail.next = p.next;
if (last == p)
last = trail;
if (count.getAndDecrement() == capacity)
notFull.signal();
}
/**
* 遍历队列,如果队列中包含o,则返回true
*/
public boolean contains(Object o) {
if (o == null) return false;
// 遍历过程中防止插入/删除,要将两个锁都锁上
fullyLock();
try {
for (Node<E> p = head.next; p != null; p = p.next)
if (o.equals(p.item))
return true;
return false;
} finally {
fullyUnlock();
}
}
构造方法
/**
* 创建一个“有界”队列(队列大小为Integer.MAX_VALUE)
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
/**
* 通过给定容量创建一个有界队列
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
/**
* 创建一个容量为Integer.MAX_VALUE的队列,并将集合c中的元素添加到队列中
*/
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
// 因为是构造方法,这里锁是为了保证可见性而不是互斥性
putLock.lock();
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
enqueue(new Node<E>(e));
++n;
}
count.set(n);
} finally {
putLock.unlock();
}
}
插入
元素入队操作的整体逻辑大致如下:
- 先获得putLock
- 检查队列是否已满
- 如果已满,则根据不同方法进行处理(返回,阻塞,等待超时等)
- 如果有剩余容量,则进行下面的逻辑
- 元素入队
- 队列中元素数量 +1
- 如果入队后队列仍有空余,唤醒在notFull条件等待的其他生产者线程
- 如果队列之前为空,插入后唤醒在notEmpty条件等待的消费者线程。
/**
* 在队列尾部插入元素,如果没有可用空间,则阻塞。
*/
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 {
while (count.get() == capacity) {
// 如果队列已满,则在notFull条件等待,直到其他
// 线程从队列中取出元素后,释放notFull条件,并
// 唤醒该线程
notFull.await();
}
// 入队
enqueue(node);
// 元素数量 +1,并返回之前的元素数量
c = count.getAndIncrement();
// 如果 插入元素后,数量仍然小于容量
// 则释放notFull条件
if (c + 1 < capacity)
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
// 如果在插入前元素数量为0,代表之前队列为空
// 插入元素后,队列不为空了,释放notEmpty条件
// 唤醒其他阻塞在notEmpty等待的线程
signalNotEmpty();
}
/**
* 插入给定元素e到队列尾部。如果插入时队列已满,则阻塞直到
* 1. 队列有空间
* 2. 超时退出
*/
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));
// 元素数量 +1,并返回之前的元素数量
c = count.getAndIncrement();
if (c + 1 < capacity)
// 如果 插入元素后,数量仍然小于容量
// 则释放notFull条件
notFull.signal();
} finally {
putLock.unlock();
}
if (c == 0)
// 如果在插入前元素数量为0,代表之前队列为空
// 插入元素后,队列不为空了,释放notEmpty条件
// 唤醒其他阻塞在notEmpty等待的线程
signalNotEmpty();
return true;
}
/**
* 插入给定元素e到队列尾部。如果插入时队列已满,则返回false
*/
public boolean offer(E e) {
if (e == null) throw new NullPointerException();
final AtomicInteger count = this.count;
if (count.get() == capacity)
// 如果队列已满,直接返回false
return false;
int c = -1;
Node<E> node = new Node<E>(e);
final ReentrantLock putLock = this.putLock;
putLock.lock();
try {
// 因为两次判断队列是否已满中间可能存在
// 其他的并发插入。所以需要重新判断
if (count.get() < capacity) {
enqueue(node);
c = count.getAndIncrement();
if (c + 1 < capacity)
notFull.signal();
}
} finally {
putLock.unlock();
}
if (c == 0)
// 如果在插入前元素数量为0,代表之前队列为空
// 插入元素后,队列不为空了,释放notEmpty条件
// 唤醒其他阻塞在notEmpty等待的线程
signalNotEmpty();
// c开始赋值为-1,只有当入队成功时,才可能>=0
return c >= 0;
}
删除/查询
元素出队操作的整体逻辑大致如下:
- 先获得takeLock
- 检查队列是否为空
- 如果为空,则根据不同方法进行处理(返回,阻塞,等待超时等)
- 如果有元素存在,则进行下面的逻辑
- 元素出队
- 队列中元素数量 -1
- 如果出队后队列中仍有元素,唤醒在notEmoty条件等待的其他消费者线程
- 如果队列之前已满,出队后唤醒在notFull条件等待的生产者线程。
/**
* 删除队列头并返回,如果队列为空,则阻塞直到有其他线程
* 向队列内添加元素,释放notEmpty条件
*/
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();
// 队列中元素数量-1,并获取之前的容量
c = count.getAndDecrement();
if (c > 1)
// 如果之前的容量大于1,说明队列不为空。
// 则唤醒其他等待读的线程
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
// 如果之前队列已满,释放notFull条件
signalNotFull();
return x;
}
/**
* 删除队列头并返回,如果队列为空,则阻塞直到
* 1. 其他线程向队列内插入元素
* 2. 超时返回null
*/
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;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
if (nanos <= 0)
// 超时返回null
return null;
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
/**
* 删除队列头并返回,如果队列为空则返回null
*/
public E poll() {
final AtomicInteger count = this.count;
if (count.get() == 0)
return null;
E x = null;
int c = -1;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
// 因为两次判断队列是否已满中间可能存在
// 其他的并发删除。所以需要重新判断
if (count.get() > 0) {
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
}
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
return x;
}
/**
* 返回队列头但不删除,如果队列为空则返回null
*/
public E peek() {
if (count.get() == 0)
return null;
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try {
Node<E> first = head.next;
if (first == null)
return null;
else
return first.item;
} finally {
takeLock.unlock();
}
}
/**
* 如果队列中存在一个或多个对象o,则删除队列中最早的一个o,并返回true。
* 否则返回false
*/
public boolean remove(Object o) {
if (o == null) return false;
// 因为需要遍历查找o,防止在此期间有其他插入/删除操作,
// 要将put/take锁都锁上
fullyLock();
try {
for (Node<E> trail = head, p = trail.next; p != null; trail = p, p = p.next) {
if (o.equals(p.item)) {
// 移除p
unlink(p, trail);
return true;
}
}
return false;
} finally {
fullyUnlock();
}
}
/**
* 删除所有结点
*/
public void clear() {
fullyLock();
try {
for (Node<E> p, h = head; (p = h.next) != null; h = p) {
h.next = h;
p.item = null;
}
head = last;
// assert head.item == null && head.next == null;
if (count.getAndSet(0) == capacity)
notFull.signal();
} finally {
fullyUnlock();
}
}
迭代器
private class Itr implements Iterator<E> {
/*
* 弱一致性迭代器。始终保留next item,如果hasNext()返回值为true,
* 即使被take删除了,我们仍然可以将其返回。
*/
// 下一个要访问的结点
private Node<E> current;
// 上一个访问的结点
private Node<E> lastRet;
// current.item
private E currentElement;
Itr() {
fullyLock();
try {
current = head.next;
if (current != null)
currentElement = current.item;
} finally {
fullyUnlock();
}
}
public boolean hasNext() {
return current != null;
}
/**
* Returns the next live successor of p, or null if no such.
* <p>
* Unlike other traversal methods, iterators need to handle both:
* - dequeued nodes (p.next == p)
* - (possibly multiple) interior removed nodes (p.item == null)
*/
private Node<E> nextNode(Node<E> p) {
for (; ; ) {
Node<E> s = p.next;
if (s == p)
// 说明p已经被移除,下一个结点为head.next
return head.next;
if (s == null || s.item != null)
// s==null,说明p为队尾,后继为null,直接返回
// s.item != null 说明s有效没有出队
return s;
p = s;
}
}
public E next() {
fullyLock();
try {
if (current == null)
throw new NoSuchElementException();
E x = currentElement;
lastRet = current;
// 返回下一个结点
current = nextNode(current);
currentElement = (current == null) ? null : current.item;
return x;
} finally {
fullyUnlock();
}
}
public void remove() {
if (lastRet == null)
throw new IllegalStateException();
fullyLock();
try {
Node<E> node = lastRet;
// 因为lastRet要被移除,所以重置上一次访问的结点
lastRet = null;
for (Node<E> trail = head, p = trail.next; p != null; trail = p, p = p.next) {
// 因为是单向链表,所以要遍历查找
if (p == node) {
// 移除p结点
unlink(p, trail);
break;
}
}
} finally {
fullyUnlock();
}
}
}