天天看点

Condition 接口

任意一个java对象,都拥有一组监视器方法(Object),主要包括wait()、notify()以及notifyall(){变相用于释放和申请锁},这些方法配合synchronized同步关键字,可以实现通知等待模式。Condition接口也提供类似Object的监视器方法,与lock 配合实现等待/通知模式。condition对象是由lock对象创建出来,依赖于Lock对象,一般会将condition变量作为成员变量。但其调用await()方法后,当前进程释放锁并在此等待,其他线程调用condition()对象的signal()方法,线程被唤醒。

Condition的实现分析:

ConditionObject 是同步器的内部类,每个condition对象都包含着一个等待队列,实现等待/通知等关键功能。

  1. 等待队列:同样是一个FIFO队列,每一个节点包含一个线程引用,该线程就是Condition对象上等待的进程。Condition拥有首尾节点的引用,而新增节点只需要将原有的尾节点nextWaiter指向它,并且更新尾节点即可。上述节点引用更新的过程并没有使用CAS保证,原因在于调用await()方法的线程必定是获取了锁的线程,也就是说该过程是由锁来保证线程安全的。从队列的角度来看,调用await()方法,相当于从同步队列的首节点移动到condition的等待队列中。
  2. 等待await():调用该方法的线程成功获取了锁的线程,也就是同步队列中的首节点,该方法会将当前线程构造成节点并加入等待队列中,然后释放同步状态,唤醒同步队列中的后继节点,然后当前线程会进入等待状态。当等待队列中的节点被唤醒,则唤醒节点的线程开始尝试获取同步状态。如果不是通过其他线程调用Condition.signal()方法唤醒,而是对等待线程进行中断,则会抛出InterruptedException。
  3. 通知 signal()方法:唤醒在等待队列中等待最长的的节点(首节点),通过调用同步器的enq(Node node)方法,等待队列中的头节点线程安全地移动到同步队列。当节点移动到同步队列后,当前线程再使用LockSupport唤醒该节点的线程。被唤醒后的线程,将从await()方法中的while循环中退出(isOnSyncQueue(Node node)方法返回true,节点已经在同步队列中),进而调用同步器的acquireQueued()方法加入到获取同步状态的竞争中。成功获取同步状态(或者说锁)之后,被唤醒的线程将从先前调用的await()方法返回,此时该线程已经成功地获取了锁。Condition的signalAll()方法,相当于对等待队列中的每个节点均执行一次signal()方法,效果就是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。