LinkedBlockingDueue是一种有界阻塞队列,它的底层是双向链表,所以它是双向的。也就是说,在队首队尾都可以进行插入和删除操作。这样,LinkedBlockingDueue就支持FIFO(队列)、FILO(栈)两种操作方式。
JUC框架 系列文章目录
static final class Node<E> {
E item;
Node<E> prev;
Node<E> next;
Node(E x) {
item = x;
}
}
transient Node<E> first;//队首
transient Node<E> last;//队尾
/** 大小,节点的个数 */
private transient int count;
/** 容量 */
private final int capacity;
final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
注意,一个Lock,两个Condition。
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
注意,初始化时,队列中连dummy node都没有的。所以刚初始化的队列的first
、last
都为null。
入队系列方法add、put、offer,实际都是在调用linkFirst或linkLast。仅以putFirst、putLast举例讲解。
public void putFirst(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
//上来就尝试入队first端,如果失败就进入条件队列
while (!linkFirst(node))//防止虚假唤醒
notFull.await();
} finally {
lock.unlock();
}
}
函数上来就尝试入队first端,如果失败就进入条件队列。当从notFull.await()
恢复执行时,再次尝试入队,如果失败则再次阻塞。
private boolean linkFirst(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)//size达到上限,只能返回false
return false;
Node<E> f = first;
node.next = f;
first = node;
if (last == null)//如果队列为空(last才会为null),那么不用重连prev指针,只需要更新last
last = node;
else//如果队列非空,那么需要把旧队首的prev指针指好
f.prev = node;
++count;
notEmpty.signal();//既然入队成功,notEmpty这个条件就满足了
return true;
}
linkFirst
入队成功返回true,失败返回false。只有队列非满,就一定能入队。
public void putLast(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
//上来就尝试入队last端,如果失败就进入条件队列
while (!linkLast(node))//防止虚假唤醒
notFull.await();
} finally {
lock.unlock();
}
}
函数上来就尝试入队last端,如果失败就进入条件队列。当从notFull.await()
恢复执行时,再次尝试入队,如果失败则再次阻塞。
private boolean linkLast(Node<E> node) {
// assert lock.isHeldByCurrentThread();
if (count >= capacity)//size达到上限,只能返回false
return false;
Node<E> l = last;
node.prev = l;
last = node;
if (first == null)//如果队列为空(first才会为null),那么不用重连next指针,只需要更新first
first = node;
else//如果队列非空,那么需要把旧队尾的next指针指好
l.next = node;
++count;
notEmpty.signal();//既然入队成功,notEmpty这个条件就满足了
return true;
}
linkLast
入队成功返回true,失败返回false。只有队列非满,就一定能入队。
入队系列方法remove、take、poll,实际都是在调用unlinkFirst或unlinkLast。仅以takeFirst、takeLast举例讲解。
public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
//上来就尝试出队first端,如果失败就进入条件队列
while ( (x = unlinkFirst()) == null)//防止虚假唤醒
notEmpty.await();
return x;
} finally {
lock.unlock();
}
}
函数上来就尝试出队first端,如果失败就进入条件队列。当从notEmpty.await()
恢复执行时,再次尝试出队,如果失败则再次阻塞。
private E unlinkFirst() {
// assert lock.isHeldByCurrentThread();
Node<E> f = first;
if (f == null)//如果队列为空,直接返回null
return null;
Node<E> n = f.next;
E item = f.item;//保存返回的值
//先逻辑删除,再next指向自身
f.item = null;
f.next = f; // help GC
first = n;//更新first为队首后继
if (n == null)//如果队列之前只有一个节点,更新last
last = null;
else//如果队列之前不只有一个节点,让队首的prev为null
n.prev = null;
--count;
notFull.signal();//既然出队成功,notFull这个条件就满足了
return item;
}
unlinkFirst
出队成功返回非null,失败返回null。只有队列非空,就一定能出队。
public E takeLast() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
//上来就尝试出队last端,如果失败就进入条件队列
while ( (x = unlinkLast()) == null)//防止虚假唤醒
notEmpty.await();
return x;
} finally {
lock.unlock();
}
}
函数上来就尝试出队last端,如果失败就进入条件队列。当从notEmpty.await()
恢复执行时,再次尝试出队,如果失败则再次阻塞。
private E unlinkLast() {
// assert lock.isHeldByCurrentThread();
Node<E> l = last;
if (l == null)//如果队列为空,直接返回null
return null;
Node<E> p = l.prev;
E item = l.item;//保存返回的值
//先逻辑删除,再prev指向自身
l.item = null;
l.prev = l; // help GC
last = p;//更新last为队尾前驱
if (p == null)//如果队列之前只有一个节点,更新first
first = null;
else//如果队列之前不只有一个节点,让队尾的next为null
p.next = null;
--count;
notFull.signal();//既然出队成功,notFull这个条件就满足了
return item;
}
unlinkLast
出队成功返回非null,失败返回null。只有队列非空,就一定能出队。
public boolean removeFirstOccurrence(Object o) {
if (o == null) return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
//从队首遍历到队尾
for (Node<E> p = first; p != null; p = p.next) {
if (o.equals(p.item)) {//找到了节点
unlink(p);
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
从队首遍历到队尾,如果找到了节点,则执行unlink
。
void unlink(Node<E> x) {
// assert lock.isHeldByCurrentThread();
Node<E> p = x.prev;
Node<E> n = x.next;
if (p == null) {//如果是队首
unlinkFirst();
} else if (n == null) {//如果是队尾
unlinkLast();
} else {//如果是内部节点(此时队列中,必有三个节点,因为两个节点都会走上面的分支)
p.next = n;
n.prev = p;
x.item = null;
// 注意我们不会去操作内部删除节点的指针,这样方便迭代器继续使用。
// 因为现在 从队列节点到内部删除节点 不可达,但内部删除节点到队列节点 可达
--count;
notFull.signal();
}
}
public boolean removeLastOccurrence(Object o) {
if (o == null) return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
for (Node<E> p = last; p != null; p = p.prev) {
if (o.equals(p.item)) {
unlink(p);
return true;
}
}
return false;
} finally {
lock.unlock();
}
}
完全对称,只是从队尾开始遍历。
迭代器是弱一致性。
private abstract class AbstractItr implements Iterator<E> {
Node<E> next;//下一次next()的值提前保存
E nextItem;//下一次next()的值提前保存
private Node<E> lastRet;//用来实现remove
abstract Node<E> firstNode();//抽象方法,获得遍历起点
abstract Node<E> nextNode(Node<E> n);//抽象方法,根据前进方向获得下一个节点
AbstractItr() {
// set to initial position
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {//初始化就准备好next的两个成员
next = firstNode();
nextItem = (next == null) ? null : next.item;
} finally {
lock.unlock();
}
}
private Node<E> succ(Node<E> n) {
for (;;) {
Node<E> s = nextNode(n);
if (s == null)//遍历到头了
return null;
else if (s.item != null)//得到一个有效节点,则返回
return s;
else if (s == n)//如果发送节点已经被unlink,则跳转到first
return firstNode();
//执行到这里,n有后继,但n的item为null,所以后移n,继续找
else
n = s;
}
}
void advance() {
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
// assert next != null;
next = succ(next);//得到后继
nextItem = (next == null) ? null : next.item;
} finally {
lock.unlock();
}
}
public boolean hasNext() {
return next != null;
}
public E next() {
if (next == null)
throw new NoSuchElementException();
lastRet = next;
E x = nextItem;
advance();//准备下一个next()的数据,这个函数才开始加锁
return x;
}
public void remove() {
Node<E> n = lastRet;
if (n == null)
throw new IllegalStateException();
lastRet = null;
final ReentrantLock lock = LinkedBlockingDeque.this.lock;
lock.lock();
try {
if (n.item != null)//如果它还没有被删除
unlink(n);
} finally {
lock.unlock();
}
}
}
正向迭代器和反向迭代器只需要实现两个抽象方法。
/** Forward iterator */
private class Itr extends AbstractItr {
Node<E> firstNode() { return first; }
Node<E> nextNode(Node<E> n) { return n.next; }
}
/** Descending iterator */
private class DescendingItr extends AbstractItr {
Node<E> firstNode() { return last; }
Node<E> nextNode(Node<E> n) { return n.prev; }
}