Java多线程Condition接口原理介绍

Condition接口提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的

Java多线程Condition接口原理介绍

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对象作为成员变量。当调用await()方法后,当前线程会释放锁并在此等待,而其他线程调用Condition对象的signal()方法,通知当前线程后,当前线程才从await()方法返回,并且在返回前已经获取了锁。

 /**
  * 当前线程进入等待状态直到被通知(signal)或中断,当前线程进入后台运行状态且从await()方法返回
  * 其他线程调用该Condition的signal或者signalAll方法,而当前线程被选中唤醒
  * 1、其他线程(interrupt)中断当前线程
  * 2、如果当前等待线程从await方法返回,那么表明当前线程已经获取了Condition对象的锁
  */
 void await() throws InterruptedException;

 /**
  * 当前线程进入等待状态直到被通知,对中断不响应
  */
 void awaitUninterruptibly();

 /**
  * <pre> {@code
  * boolean aMethod(long timeout, TimeUnit unit) {
  * long nanos = unit.toNanos(timeout);
  * lock.lock();
  * try {
  *  while (!conditionBeingWaitedFor()) {
  *  if (nanos <= 0L)
  *   return false;
  *  nanos = theCondition.awaitNanos(nanos);
  *  }
  *  // ...
  * } finally {
  *  lock.unlock();
  * }
  * }}</pre>
  * 当前线程进入等待状态直到被通知、中断或超时。返回值表示剩余时间,如果在nanosTimeout纳秒之前被唤醒,那么返回值就是nanosTimeout-实际耗时
  * 返回值<=0说明超时
  * 
  */
 long awaitNanos(long nanosTimeout) throws InterruptedException;

 /**
  * 当前线程进入等待状态直到被通知、中断或超时,如果没有到指定时间被通知返回true,否则返回false
  */
 boolean await(long time, TimeUnit unit) throws InterruptedException;

 /**
  * 唤醒一个等待在Condition上的线程,该线程从等待方法返回之前必须获得与Condition相关联的锁
  */
 void signal();

获取一个Condition必须通过Lock的newCondition()方法。下面通过一个有界队列的示例来深入了解Condition的使用方式。

有界队列是一种特殊的队列,当队列为空时,队列的获取操作将会阻塞获取线程,直到队列中有新增元素,当队列已满时,队列的插入操作将会阻塞插入线程,直到队列出现“空位”

public class BoundedQueue<T> {
 private Object[] items;

 // 添加的下标,删除的下标和数组当前数量
 private int addIndex,removeIndex,count;
 private Lock lock = new ReentrantLock();
 private Condition notEmpty = lock.newCondition();
 private Condition notFull = lock.newCondition();
 public BoundedQueue(int size){
  items = new Object[size];
 }

 /**
  * 添加一个元素,如果数组满,则添加线程进入等待状态,直到有"空位"
  * @author fuyuwei
  * 2017年5月21日 下午6:14:55
  * @param t
  * @throws InterruptedException
  */
 public void add(T t) throws InterruptedException{
  lock.lock();
  try{
   while(count == items.length){
    notFull.await();
   }
   items[addIndex] = t;
   if(++addIndex == items.length)
    addIndex = 0;
   ++count;
   notEmpty.signal();
  }finally{
   lock.unlock();
  }
 }

 /**
  * 由头部删除一个元素,如果数组空,则删除线程进入等待状态,直到有新添加元素
  * @author fuyuwei
  * 2017年5月21日 下午6:20:54
  * @return
  * @throws InterruptedException
  */
 @SuppressWarnings("unchecked")
 public T remove() throws InterruptedException{
  lock.lock();
  try{
   while(count == 0)
    notEmpty.await();
   Object x = items[removeIndex];
   if(++removeIndex == items.length)
    removeIndex = 0;
   --count;
   notFull.signal();
   return (T)x;
  }finally{
   lock.unlock();
  }
 }
}

首先需要获得锁,目的是确保数组修改的可见性和排他性。当数组数量等于数组长度时,表示数组已满,则调用notFull.await(),当前线程随之释放锁并进入等待状态。如果数组数量不等于数组长度,表示数组未满,则添加元素到数组中,同时通知等待在notEmpty上的线程,数组中已经有新元素可以获取。

Java多线程Condition接口原理介绍

扫一扫手机访问