在使用Lock之前,我们使用的最多的同步方式应该是synchronized关键字来实现同步方式了。配合Object的wait()、notify()系列方法可以实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。

1、Condition 的构建
首先我们需要明白condition对象是依赖于lock对象的,意思就是说condition对象需要通过lock对象进行创建出来(调用Lock对象的newCondition()方法)。consition的使用方式非常的简单。但是需要注意在调用方法前获取锁。condition可以通俗的理解为条件队列。当一个线程在调用了await方法以后,直到线程等待的某个条件为真的时候才会被唤醒。这种方式为线程提供了更加简单的等待/通知模式。Condition必须要配合锁一起使用,因为对共享状态变量的访问发生在多线程环境下。一个Condition的实例必须与一个Lock绑定,因此Condition一般都是作为Lock的内部实现。
- await() :造成当前线程在接到信号或被中断之前一直处于等待状态。
- await(long time, TimeUnit unit) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
- awaitNanos(long nanosTimeout) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout - 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。
- awaitUninterruptibly() :造成当前线程在接到信号之前一直处于等待状态。
- awaitUntil(Date deadline) :造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回false。
- signal() 唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
- signalAll() 唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的
2、Condition 使用范式
public class ConditionUseCase {public Lock lock = new ReentrantLock();public Condition condition = lock.newCondition();// 等待通知,相当于Object#wait()public void conditionWait() {lock.lock();try {condition.await();}catch (Exception e){}finally {lock.unlock();}}// 发送通知,相当于Object#notify() 或Object#notifyAll();public void conditionSignal() {lock.lock();try {Thread.sleep(5000);condition.signal();}catch (Exception e){}finally {// 锁释放的时候才会唤醒线程,在signal()的时候并不会唤醒线程,这和Synchronized类似lock.unlock();}}}
3、Condition 原理浅析
3.1 等待队列
等待队列是一个FIFO的队列,在队列中的每个节点都包含了一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用了Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列并进入等待状态。
一个Condition包含一个等待队列,Condition拥有首节点(firstWaiter)和尾节点(lastWaiter)。当前线程调用Condition.await()方法,将会以当前线程构造节点,并将节点从尾部加入等待队列,等待队列的基本结构如下图所示

在Object的监视器模型上,一个对象拥有一个同步队列和等待队列,而并发包中的Lock(更确切地说是同步器),我们可以通过方法名newCondition() 可以知道每次调用都会生成一个新的Condition对象,所以一个AQS 拥有一个同步队列和多个等待队列,其对应关系如下图所示

3.2 等待状态
调用Condition的await()方法(或者以await开头的方法),会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()方法返回时,当前线程一定获取了Condition相关联的锁,这个Synchronized的原理类似。如果从队列(同步队列和等待队列)的角度看await()方法,当调用await()方法时,相当于同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中。
调用该方法的线程成功获取了锁的线程,也就是同步队列中的首节点,该方法会将当前线程构造成节点并加入等待队列中,然后释放同步状态,唤醒同步队列中的后继节点,然后当前线程会进入等待状态。
3.3 通知状态恢复
调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将节点移到同步队列中。
所以调用__signal()方法并不会恢复线程,而是转移到同步队列中,和其他线程节点继续竞争锁,成功获取到锁之后,await()方法将返回即系执行。
_
4、参考文章
- Java多线程Condition接口原理详解
- Java并发之Condition
- 《Java并发编程的艺术》
_
