-
条件队列可以构建高效、高响应性的状态依赖类(wait时自动挂起, 不用像轮询方式一样费尽心机的选择sleep时间), 但是__容易不正确使用__
所以, 如果可能的话, 还是__尽可能用LinkedBlockingQueue, Latch, Semephore, FutureTask等__
-
条件谓词
(1) 定义
使某个操作成为状态依赖操作的前提条件
例如, BlockingQueue的take方法的条件谓词是: 缓存不为空
(2) 条件等待的三元组:
加锁、 条件谓词、 wait()方法
-
过早唤醒
(1) wait()方法的返回__未必表示线程正在等待的条件谓词已经为真__
1° 同一个条件队列可以关联多个条件谓词, 唤醒可能来自其他的条件谓词成真, 而不是等待的那个条件谓词成真
例如BoundedBuffer的this作为一个条件队列, 关联了2个条件谓词: 缓存不为空 / 缓存不为满
2° wait()可以假装返回
(2) 发出通知的线程调用notifyAll时条件谓词为真, 到重新获取锁时又变成了假
因此, 结论是每当线程从wait()返回后, 都要再次检查条件谓词是否为真
-
使用条件等待时的原则(Object.wait或Condition.await)
(1) 线程在执行前必须通过条件谓词为真的测试
(2) 在调用wait()之前测试条件谓词, 在wait()返回之后还要测试条件谓词
(3) 在while循环中调用wait(), 直到条件满足
(4) 确保使用与条件队列相关的锁来保护构成条件谓词的各个状态变量
(5) 调用wait, notify, notifyAll时确保持有条件队列相关的锁
(6) 在检查条件谓词之后、开始执行相应的操作之前, 不能释放锁
-
信号丢失
示例: 一个条件队列关联两个条件谓词A、B, 两个线程分别等待A、B成真, 另一个线程调用notify(), 等待A的线程被唤醒, 重新测试条件谓词发现是A没有成真, 而是B成真导致发出的notify信号, 所以等待A的线程继续wait; 而本应该被唤醒的等待B的线程没有唤醒信号, 所以它只能继续等
结论: 尽可能使用notifyAll, 而不是notify
但是, notifyAll更低效, 因为绝大部分被唤醒的线程很可能继续等, 而且它们还要竞争锁, 但是要先保证程序运行正确再考虑优化, 所以还是先用notifyAll
-
什么时候可以放心大胆的使用notify
同时满足以下两个条件:
1° 所有等待线程的类型相同(不能有些线程等待缓存非空, 有些线程等待缓存非满)
2° 在条件变量上的每次通知, 最多只能唤醒一个线程来执行(例如生产好一个产品之后, 只能有一个消费者线程消费)
-
入口协议: 某个操作的条件谓词
出口协议: 该操作修改的所有状态变量, 并确认它们是否可以使其他的条件谓词变为真, 如果为真则notify
chapter14_构建自定义的同步工具_2_使用条件队列
最新推荐文章于 2024-01-12 16:56:31 发布