chapter14_构建自定义的同步工具_2_使用条件队列

时间:2019-03-19
本文章向大家介绍chapter14_构建自定义的同步工具_2_使用条件队列,主要包括chapter14_构建自定义的同步工具_2_使用条件队列使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
  • 条件队列可以构建高效、高响应性的状态依赖类(wait时自动挂起, 不用像轮询方式一样费尽心机的选择sleep时间), 但是__容易不正确使用__

    所以, 如果可能的话, 还是__尽可能用LinkedBlockingQueue, Latch, Semephore, FutureTask等__

  • 条件谓词

    (1) 定义

    使某个操作成为状态依赖操作的前提条件

    例如, BlockingQueue的take方法的条件谓词是: 缓存不为空

    (2) 条件等待的三元组:

    加锁、 条件谓词、 wait()方法

  • 过早唤醒

    (1) wait()方法的返回__未必表示线程正在等待的条件谓词已经为真__

    同一个条件队列可以关联多个条件谓词, 唤醒可能来自其他的条件谓词成真, 而不是等待的那个条件谓词成真

    例如BoundedBuffer的this作为一个条件队列, 关联了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