1.3 使用 Condition
为了解决线程的“假死”问题,我们可以使用 Condition
来尝试实现一下,Condition
是 JUC(java.util.concurrent)包下的类,需要使用 Lock
锁来创建,Condition
提供了 3 个重要的方法:
await
:对应wait
方法;
signal
:对应notify
方法;
signalAll
:notifyAll
方法。
Condition
的使用和 wait/notify
类似,也是先获得锁然后在锁中进行等待和唤醒操作,Condition
的基础用法如下:
// 创建 Condition 对象 Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // 加锁 lock.lock(); try { // 业务方法.... // 1.进入等待状态 condition.await(); // 2.唤醒操作 condition.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); }
小知识:Lock的正确使用姿势
切记 Lock
的 lock.lock()
方法不能放入 try
代码中,如果 lock
方法在 try
代码块之内,可能由于其它方法抛出异常,导致在 finally
代码块中, unlock
对未加锁的对象解锁,它会调用 AQS
的 tryRelease
方法(取决于具体实现类),抛出 IllegalMonitorStateException
异常。
回归主题
回到本文的主题,我们如果使用 Condition
来实现线程的通讯就可以避免程序的“假死”情况,因为 Condition
可以创建多个等待集,以本文的生产者和消费者模型为例,我们可以使用两个等待集,一个用做消费者的等待和唤醒,另一个用来唤醒生产者,这样就不会出现生产者唤醒生产者的情况了(生产者只能唤醒消费者,消费者只能唤醒生产者)这样整个流程就不会“假死”了,它的执行流程如下图所示:
了解了它的基本流程之后,咱们来看具体的实现代码。
基于 Condition
的工厂实现代码如下:
class FactoryByCondition { private int[] items = new int[1]; // 数据存储容器(为了演示方便,设置容量最多存储 1 个元素) private int size = 0; // 实际存储大小 // 创建 Condition 对象 private Lock lock = new ReentrantLock(); // 生产者的 Condition 对象 private Condition producerCondition = lock.newCondition(); // 消费者的 Condition 对象 private Condition consumerCondition = lock.newCondition(); /** * 生产方法 */ public void put() throws InterruptedException { // 循环生产数据 do { lock.lock(); while (size == items.length) { // 注意不能是 if 判断 // 生产者进入等待 System.out.println(Thread.currentThread().getName() + " 进入阻塞"); producerCondition.await(); System.out.println(Thread.currentThread().getName() + " 被唤醒"); } System.out.println(Thread.currentThread().getName() + " 开始工作"); items[0] = 1; // 为了方便演示,设置固定值 size++; System.out.println(Thread.currentThread().getName() + " 完成工作"); // 唤醒消费者 consumerCondition.signal(); try { } finally { lock.unlock(); } } while (true); } /** * 消费方法 */ public void take() throws InterruptedException { // 循环消费数据 do { lock.lock(); while (size == 0) { // 消费者阻塞等待 consumerCondition.await(); } System.out.println("消费者工作~"); size--; // 唤醒生产者 producerCondition.signal(); try { } finally { lock.unlock(); } } while (true); } }