测试结果执行结果:t3 的第一个 put 操作会因为缓存已满而阻塞,5 秒后主线程删除两个操作后,重新被唤醒 。主线程的第四个 bu.take() 操作会因为缓存为空而阻塞,直到 t3 在 50 秒后重新插入"last one" 后被唤醒,操作结束 。
Tue Dec 20 10:23:53 CST 2019 buffer is Full thread wait:Thread-2Tue Dec 20 10:23:58 CST 2019 main take:hello1Tue Dec 20 10:23:58 CST 2019 main take:hello2Tue Dec 20 10:23:58 CST 2019 buffer is empty thread wait:mainTue Dec 20 10:23:58 CST 2019 main take:hello3Tue Dec 20 10:23:58 CST 2019 buffer is empty thread wait:mainTue Dec 20 10:24:48 CST 2019 main take:last one...Tue Dec 20 10:24:48 CST 2019 main over...
启示录我们的例子中,“非空” 和 “非满” 这两种条件关联着同一个条件队列,当一个线程由于其他线程调用了notifyAll 而被唤醒时,并不意味着它等待的条件已经为真了,这也是内置条件队列的局限所在 。
【Java内置条件队列应用,实现经典的生产者消费者算法】所以代码中的加固措施是,使用循环判断条件是否发生,如果发生,则调用 wait 阻塞自己,等待其他线程唤醒:
while(isFull()){ System.out.println(new Date()+" buffer is Full thread wait:"+Thread.currentThread().getName()); wait();}
同样的功能,Java 并发包中的 ArrayBlockingQueue 是使用 ReentrantLock 和 ObjectCondition 实现的可阻塞队列,为什么 JDK 使用显式锁和显式条件队列呢?
使用内置锁的局限性在于一把锁只有一个条件队列,而这里涉及到两种等待条件,所以使用 ReentrantLock 更合适,它可以关联多个条件队列,这样就可以巧妙地处理多条件的阻塞和唤醒了!
推荐阅读
- java 异步编程
- 公务科员提副科条件是什么?
- java中常见的六种线程池详解
- 用 Java 训练深度学习模型,原来能这么简单
- 手机首付分期付款条件是什么?
- 征途手游以下哪项不是升级技能的必要条件的答案
- Java 如何获得一个文件或文件夹的大小
- Java开发人员必知的常用类库,这些你都知道吗?
- Java反射有多强?它拥有这五大神奇功能
- Java如何防止接口重复提交