条件变量:线程进入临界区域后,往往需要等待满足某个条件才能继续执行。例如,消费者需要等待缓冲区有产品后才能消费,用户只有在银行账户上有金额的时候能能取款等。 在JDK1.5之前,主要用wait()、notify()和notifyAll()方法执行条件操作中的等待和唤醒机制,JDK1.5之后版本开始,引入了条件变量(Condition Variables)。条件变量也被称为条件队列(Condition Queue)。是由Condition定义的, 它可以让一个线程在条件不满足的情况下一直等下去,直到由其它线程唤醒它。 接口Condition 与JDK1.5之前的版本 wait()、notify()和notifyAll()方法的不同,主要由四点: 1)它允许在一个对象上有多个等待集合。 2)当synchronized锁被Lock锁对象替换后,相应对象的监视器方法wait()、notify()和notifyAll()操作也要用await()、signal()和signalAll()方法操作替换。 3)Condition对象与对象监视器上的方法wait()、notify和notifyAll()的行为和语义不同。Condition对象在唤醒线程的次序上与对象监视器上的方法不同,并且唤醒时不要求一定持有Lock对象锁。 4)Condition接口的实例是一个对象,在synchronized控制的范围内,可以在Condition对象实例上条用方法wait、notify()和notifyAll()。 由于要对共享状态进行操作,所以Codition对象一般是要包含在同步操作中,Lock对象为Condition提供了这种同步操作。Condition对象通常被绑定到Lock对象上,要创建一个Condition对象的实例,须用Lock方法 newCondition(); 例如: Lock lock = new ReentrantLock(); Condition notfull = lock.newCondition(); Condition empty = lock.newCondition(); 接下来是Condition接口的方法: 1、await()方法:定义如下 void await() throws InterruptedExcetion 当一个线程调用了await()方法后,线程进入阻塞状态,这时线程本省是无法解除这种状态的,需要其他的线程来帮忙,否则线程永远无法执行。阻塞状态解除的条件: 1)某一个线程在当前条件对象上调用了方法signal()方法,而该线程正好被选中唤醒。 2)某个线程在当前条件对象上调用了signalAll()方法。 3)其他线程调用此线程的interrput()方法,打断可此线程。 调用await()方法之前。当前线程应该持有与Condition相关联的锁,否则会抛出IllegalMonitorException异常。 一般来说,对await()方法的调用总是包含在一个循环结构中,并且await操作要放入try .... catch...语句块中。 例如: while(条件){ try{ condition.await(); }catch(InterruptedException e){ e.printStackTrace(); } } 2、signal()方法: signal()方法用于唤醒一个正在等待的线程,如果调用了此方法,则在当前条件对象上等待的线程中选择一线程进行唤醒,该线程在await()方法返回前仍需要重新获取对象锁,如果获取该锁程成功,则离开await()方法,否则将继续等待。 3、signalAll()方法: signalAll()方法用于唤醒所有正在等待的线程。如果调用的此方法,则在当前条件对象等待的所有线程将被唤醒,被唤醒的线程将竞争获取锁,只有获得锁的线程才能离开await()方法,其他没有获取锁的线程则继续等待。
Demo示例: 使用条件对象实现多缓冲区生产消费者问题。 分析: 将多缓冲区生产消费问题中的wait().notifyAll()方法,替换为await()和signalAll()方法。只是替换时需要使用LocK对象,并且在该对象上定义相应的条件对象。
public class CuddyHole{ private int[] goods;//缓冲区 private int front;//头指针 private int rear;//尾指针 private int nbuf;//缓冲区大小 private int count;//缓冲区的数据的数量 private Lock lock = new ReentrantLock();// Condition full = lock.newCondition(); Condition empty = lock.newCondition(); public CubbyHole(int nbuf){ front = 0; rear = 0; count = 0; this.nbuf = nbuf; goods = new int[nbuf]; } //取数据 public int get(int id){ lock.lock(); try{ while(count<=0){ try{ empty.await(); }catch(InterruptedException e){ e.printStackTrace(); } } front = (front+1)%nbuf; System.out.println("第"+id+"号消费者拿走了数据"+goods[front]); count--; full.signal(); return goods[front]; }finally{ lock.unlock(); } } //存数据 public void put(int value,int id){ lock.lock(); try{ while(count>=nbuf){ try{ full.await(); }catch(InterruptedException e){ e.printStackTrace(); } } rear = (rear+1)%nbuf; goods[rear] = value; System.out.println("第"+id+"号生产者生产了数据"+value); count++; empty.signal(); }finally{ lock.unlock(); } } } public class Consumer extends Thread{ private CubbyHole cubbyHole; private int id; public Consumer(CubbyHole cubbyHole,int id ){ this.cubbyHole = cubbyHole; this.id = id; } public void run(){ for(int i=0;i<50;i++){ cubbyHole.get(id); } } } public class Producer extends Thread{ private CubbyHole cubbyHole; private int id; public Producer( CubbyHole cubbyHole,int id){ this.cubbyHole = cubbyHole; this.id = id ; } public void run(){ for(int i=0;i<50;i++){ cubbyHole.put((int)(Math.random()*100),id); } } } public class Index{ private static int np = 4; private static int nc = 4; private static int nbuf =10; public static void main(String [] args){ CubbyHole cubbyHole = new CubbyHole(nbuf); Producer [] producer = new Producer[np]; for(int i=0;i<producer.length;i++){ producer[i] = new Producer(cubbyHole,i+1); producer[i].start(); } Consumer [] consumer = new Consumer[nc]; for(int i=0;i<consumer.length;i++){ consumer[i] = new Consumer(cubbyHole,i+1); consumer[i].start(); } try{ for(int i=0;i<np;i++){ producer[i].join(); } for(int i=0;i<nc;i++){ consumer[i].join(); } }catch(InterruptedException e){ e.printStackTrace(); } } }