第十四章 构建自定义的同步工具
14.1 状态依赖性管理
基于先检查后执行的状态依赖性操作在多线程下常常发生一些我们不希望的结果.因此有必要对状态依赖操作进行管理,
- 构成前提条件的状态变量必须有对象的锁来保护,从而使他们在测试前提条件的同事保持不变.
- 如果条件尚未满足, 则必须释放锁.
- 下次测试前提条件之前则必须重新获取锁.之后再进行前提条件的测试
重试的实现方式 :
- 自旋等待. 在条件不成立时一直询问, 直到条件成立. 会消耗大量的CPU时间
- 休眠. 如果条件不成立, 则休眠一段时间, 休眠过后继续测试条件是否成立. 响应性较低
- Thread.yield() 在条件不成立时,让其他线程优先执行
14.2 条件队列
使得一组线程能够通过某种方式来等待特定条件变为真.队列中的元素为线程.
每个Java对象都可以作为一个锁, 每个锁都可以拥有一个条件队列. 内置锁的条件队列是非公平的.
Object.wait() 挂起当前线程. 并自动释放锁,
Thread.sleep() 挂起当前线程, 不会释放锁
14.2.1 条件谓词
条件谓词是某个状态依赖操作的前提条件.
条件等待的三元关系 : 加锁, 线程挂起, 条件谓词
条件谓词包含的状态变量必须由同一个锁来保护, 测试条件之前必须先持有这个锁, 锁对象和条件队列对象必须是同一个对象.
使用条件等待:
- 至少有一个条件谓词
- 在一个循环中调用wait()
- 在wait()返回后再次测试条件谓词是否成立
14.2.2 通知与唤醒
仅在满足一下两种情况事才可以使用 notify()
- 单进单出. 条件变量的每次通知最多只能唤醒一个线程执行.否则会导致信号的丢失,使得某个线程等待一个发生过的信号
- 所有等待线程的类型都相同. 每个线程在wait()返回后执行相同的操作.且只有一个条件谓词与条件队列相关.
notify()比notifyall() 更高效,但是也更加危险
通过减少比必要的通知次数可以优化通知的性能.
14.3 显式的 Condition 对象
同一个条件队列上等待不同条件谓词的情况
如果文章对您有所帮助,可以点一下推荐