条件变量:线程进入临界区域后,往往需要等待满足某个条件才能继续执行。例如,消费者需要等待缓冲区有产品后才能消费,用户只有在银行账户上有金额的时候能能取款等。
在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();
}
}
}