一、Condition简介
Java对象的wait()、notify()以及notifyAll()方法,这些方法与synchronized关键字配合,可以实现等待/通知模式。Condition接口也提供了类似功能,与Lock配合可以实现等待/通知模式,但是两者还是有一些区别的。
对比项 | Object Moitor | Condition |
---|---|---|
前置条件 | 获取对象的锁 | 调用Lock.lock()获取锁调用lock.newCondition()获取Condition |
调用方式 | object.wait() | condition.await() |
等待队列个数 | 1个 | 多个 |
当前线程释放锁并进入等待状态 | 支持 | 支持 |
当前线程释放锁并进入等待状态,在等待状态中响应中断 | 不支持 | 支持 |
当前线程释放锁并进入超时等待状态 | 支持 | 支持 |
当前线程释放锁并进入等待状态到将来的某个时间 | 不支持 | 支持 |
唤醒等待队列中的一个线程 | 支持 | 支持 |
唤醒等待队列中的全部线程 | 支持 | 支持 |
二、Condition的实现原理
每个Condition对象都包含了一个FIFO队列,称为等待队列,该队列是实现等待/通知功能的关键。
2.1 等待队列
队列中的每个节点都包含了线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用了condition.await()方法,那么该线程会释放锁、构造节点加入等待队列尾部。同步队列和等待队列中节点类型都是同步器的静态内部类AbstractQueuedSynchronized.Node。等待队列的基本结构如图:
2.2 等待
从队列的角度看await()方法,当调用await()方法时,相当于同步队列的首节点(当前获取锁的线程)移动到Condition的等待队列末端。
但是实际上同步队列的首节点并不会直接加入到等待队列,而是通过addConditionWaiter()方法把当前线程构造成一个新的节点并加入等待队列中。代码如下:
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 构造一个新的Node节点,加入到等待队列末段
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
// 将当前线程挂起
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
2.3 唤醒
调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点,也就是首节点,在唤醒节点之前,会将节点移到同步队列末端。
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
final boolean transferForSignal(Node node) {
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 将节点加到同步队列末端
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。