Condition接口提供了类似Object的监视器方法,可以与Lock配合实现等待/通知模式.
Object监视器方法与Condition接口对比
| 对比项 | Object Monitor Methods | Condition接口 |
|---|---|---|
| 前置条件 | 获取对象锁 | 调用Lock.lock()获取锁 调用Lock.neiCondition()获取Condition对象 |
| 调用方式 | 直接调用,如:object.wait() | 直接调用,如condition.await() |
| 等待队列个数 | 一个 | 多个 |
| 当前线程释放锁进入等待状态 | 支持 | 支持 |
| 当前线程释放锁进入等待状态,且在等待状态不响应中断 | 不支持 | 支持 |
| 当前线程释放锁进入超时等待状态 | 支持 | 支持 |
| 当前线程释放锁进入等待状态,直至某一时间 | 不支持 | 支持 |
| 唤醒等待队列中的一个线程 | 支持 | 支持 |
| 唤醒等待队列中的全部线程 | 支持 | 支持 |
condition接口
Condition定义了等待/通知两种类型的方法,当前线程调用这些方法时,需要提前获取到Condition对象关联的锁,Condition对象是由Lock对象创建出来的(通过调用Lock对象的newCondition()方法)。
也就是说,Condition是依赖Lock对象的。
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
lock.lock();
try {
//等待
condition.await();
} finally {
lock.unlock();
}
}
public void conditionSignal() throws InterruptedException {
lock.lock();
try {
// 通知
condition.signal();
} finally {
lock.unlock();
}
}分析:
- 使用:一般都会将Condition对象作为成员变量,获取一个condition必须通过Lock的newCondition方法
- await():当调用await()方法后,当前线程会释放锁并在此等待
- signal():其他线程调用Condition对象的signal()方法,通知当前线程后,当前线程才从等待状态进入锁的同步状态,尝试获取锁,获取成功则从await()方法返回
Condition实现
ConditionObject是同步器AQS的内部类,因为Condition的操作需要获取相关联的的锁,所有作为同步器的内部类也较为合理。
每个Condition对象都包含一个队列(等待队列),该队列是Condition实现等待/通知功能的关键
等待队列
等待队列是一个FIFO的队列,并且在同步队列中的节点复用了AbstractQueuedSynchronizer.Node,与同步队列中的节点一一致

分析:
- 一个Condition包含一个等待队列,Condition拥有首节点和尾节点。
- 一个同步器(AQS)拥有一个同步队列和多个等待队列,因为condition对象可以创建多个。(Object监视器模型中,一个对象只拥有一个同步队列和一个等待队列)
等待
调用Condition的await()方法,会使当前线程进入等待队列并释放锁,同时线程状态变成等待状态(WAITING)。
当await方法返回时,当前线程一定获得了Condition相关的锁。如果从队列的角度看await方法,当调用await方法时,相当于同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中。

通知
调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒之前,会将节点移动到同步队列中,加入到或偶去同步状态的竞争中。

成功获取锁之后,被唤醒的线程将从先前调用的await()方法返回,继续执行。
Condition的signalAll()方法,相当于等待队列中的每一个节点均执行一次signal方法,效果是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。