1.定义
在操作系统——多线程同步互斥一文中已详细介绍过了条件变量及其使用注意事项。条件变量,是为了解决等待同步需求,实现线程间协作通信的一种机制。条件变量用来自动阻塞一个线程,直到某特殊情况发生唤醒该线程为止。通常条件变量和锁机制同时使用。
2.Java中的条件变量
2.1 synchronized + wait()、notify()、notifyAll()
线程A(消费者):
synchronized(obj){
while(cond is false){
obj.wait(); //不满足条件,加入等待队列,自动释放锁
}
consume();
alter cond;
}
线程B(生产者):
synchronized(obj){
produce();
alter cond;
if(cond is true){
obj.notify();
}
}
为什么wait()和notify()必须放在synchronized里面?
其实这已经在“操作系统——多线程同步互斥” 条件变量那一块说过。
如果A在判断part不满足条件后,此时可能由于时间片轮换造成消费者还没来得及执行wait()操作,导致wait队列中并不存在此线程,恰巧在此时切换到了某个生产者线程中,并且恰好满足了条件,发出了cond信号,此时os便在wait队列中查找等待中的线程,但是发现并没有等待线程,所以这个cond信号便丢失了,造成后面切换到消费者线程,继续执行到wait()时,已经错过了这个信号,导致消费者丢失了此次信号,处于阻塞态。
正确做法是当线程切换导致消费者来不及进入wait队列时,其它程序也不可以操作该共享变量,既保证消费者wait()和条件判断是一个原子操作,而最常见的做法便是在条件判断到wait()的这一段代码加一个互斥锁进行保护,另外生产者操作共享资源时,必须先获得锁才行。
2.2 重入锁+Condition对象(await()、signal()、signalAll())
在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。 这样看来,Condition和传统的线程通信没什么区别,Condition的强大之处在于它可以为多个线程间建立不同的Condition。