BlockingQueue是我们在使用线程池的时候使用比较多的等待队列,这里同时借助BlockingQueue分析下AQS中的ConditionObject。
ArrayBlockingQueue
构造函数 :
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
// 构造函数中会new出一个新的ReentrantLock 方便后续使用
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition(); // 用于挂起生产节点
notFull = lock.newCondition(); // 用于挂起消费节点
}
put方法:
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
// 这里加锁保证入队的原子性
// 由于使用Interruptibly结尾的lock 所以会抛出中断异常
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await(); // 队列已满 阻塞自己
enqueue(e); 入队
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal(); // 唤醒一个消费节点
}
poll:
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 如果 队列为空 直接返回空 否则取出一个
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0; // 将takeIndex 重置为队列头部
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal(); // 唤醒一个生产节点
return x;
}
take:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await(); // 如果队列为空 会阻塞自己
return dequeue();
} finally {
lock.unlock();
}
}
可以看到,在BlockingQueue中,使用Condition做了一些阻塞操作,下面来分析下:
首先newCondition方法会生成一个ConditionObject对象,该对象是AQS中的一个内部类:
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
}
在使用condition时,AQS会维护一个ConditionObject队列,队列中记录了所有正在等待的节点,并且这些节点不会去抢锁。
然后来看下await方法,该方法的作用是将当前线程放入等待队列,并从CLH队列中取出(关于CLH队列,其实就是AQS中维护的双向链表,用于等待获取锁):
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 新建condition状态的节点并将其入队
AbstractQueuedSynchronizer.Node node = addConditionWaiter();
// 释放当前节点的锁
// 注意 这里会记录下拿了几个锁 后面加锁也需要同样的数量
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) { // 节点没有在CLH队列中里面
// //将线程进行挂起,前面已经释放掉锁了,并且已经安全的添加到了condition队列中
LockSupport.park(this);
// 执行这里的条件: 被中断 or 被前置节点唤醒
// 这里只要checkInterruptWhileWaiting 返回的是0 就继续park
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 重新抢锁 此时节点已经在CLH队列中了 获取成功后判断该线程是否发生错误
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT; // 退出时重新中断
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters(); // 清除队列中的无效节点
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode); // 如果出现异常 抛出
}
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters(); // 清理无效节点
t = lastWaiter;
}
// 新建condition类型的节点
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null) // 队列为空 则新建的节点就是头
firstWaiter = node;
else // 否则 将当前节点加入到队尾
t.nextWaiter = node;
lastWaiter = node;
return node;
}
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
// 注意 这里有个next 有个 nextWaiter
// 一个用于CLH队列 一个用于condition等待队列 要区分开来
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
*/
return findNodeFromTail(node);
}
// 获取队列尾节点
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) { // 从尾部遍历整个节点 看是否有当前节点
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
释放当前节点的所有锁:
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) { // 释放当前节点的所有锁 并唤醒后置节点
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
public final boolean release(int arg) {
if (tryRelease(arg)) { // 释放所有锁
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点
return true;
}
return false;
}
清理队列中的无效节点:
private void unlinkCancelledWaiters() {
AbstractQueuedSynchronizer.Node t = firstWaiter;
AbstractQueuedSynchronizer.Node trail = null;
while (t != null) {
AbstractQueuedSynchronizer.Node next = t.nextWaiter; // 拿到下一个节点
// 若头节点的状态已经不是CONDITION
if (t.waitStatus != AbstractQueuedSynchronizer.Node.CONDITION) {
t.nextWaiter = null; // 剔除头节点
if (trail == null)
firstWaiter = next; // 直接将 firstWaiter 记录为 next
else
// trail已经被记录为CONDITION状态的节点
// 将nextWaiter 记录为next 即:
// CONDITION -> CANCELED -> UNKNOW
// 转换为 CONDITION -> UNKNOW
trail.nextWaiter = next;
if (next == null) // 已经遍历到了队尾
lastWaiter = trail;
}
else
trail = t; // 如果当前节点还是CONDITION状态 则使用trail记录下
t = next;
}
}
该方法中有个疑点,CONDITION状态是什么时候被重置掉的 ?
其实是在await方法中 :
// 这里 线程被唤醒后 会执行checkInterruptWhileWaiting 方法
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
private int checkInterruptWhileWaiting(AbstractQueuedSynchronizer.Node node) {
// 判断节点是否还在condition队列里面,如果在,将状态变成0,放到等待返回true,抛异常。
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
final boolean transferAfterCancelledWait(Node node) {
// 也就是在这里 node 状态会被重置为0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node); // 将该节点入队
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
// 可能有别的线程通过signal 唤醒了当前节点
// 并且正在入队 那么这时 自旋啥也不干
while (!isOnSyncQueue(node))
Thread.yield();
return false; // 修改node 状态失败 返回false
}
// 这个方法应该很熟悉了 将一个节点放入CLH队列中
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
接下来看下signal方法:
public final void signal() {
// getExclusiveOwnerThread() == Thread.currentThread();
// 持有锁的线程是否是本线程,如果不是持有锁的线程直接抛异常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
AbstractQueuedSynchronizer.Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(AbstractQueuedSynchronizer.Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null) // 将头节点后移一位
lastWaiter = null; // 队列已经空了
first.nextWaiter = null; // 断开当前节点对后继节点的引用
} while (!transferForSignal(first) &&
(first = firstWaiter) != null); // 队列不为空 则继续
}
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
// 将剥离出来的节点改为0状态
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false; // 失败的话 直接返回 操作等待队列中下一个节点
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node); // 将当前节点入队 注意 这里返回的是当前节点的前驱节点
int ws = p.waitStatus;
// 如果前置节点状态大于0(被取消)
// 或者更新状态为SIGNAL 失败(SIGNAL表示后继节点可以被唤醒)
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 则直接唤醒当前线程
LockSupport.unpark(node.thread);
return true;
}
signalAll:
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null); // 主要这里不同 只要等待队列还有节点 就继续唤醒
}