并发编程实战学习笔记(九)-显式锁
为什么创建一种与内置锁如此相似的新加锁规则(显示锁)?
内置锁的局限
- 无法中断一个正在等待锁的线程,或者获取锁时不支持超时操作。
- 内置锁必须在获取锁的代码块中释放,这就简化了编码工作,并且与异常处理操作实现了很好的互动,但却无法实现非阻塞结构的加锁规则。
显示锁的优势
- 轮询锁与定时锁。
可定时的与可轮询的锁获取模式是由tryLock方法实现的,与无条件的锁获取模式相比,它具有更完善的错误恢复机制。在内置锁中,死锁是一个严重的问题,恢复程序的唯一办法是重新启动程序,而防止死锁的唯一方法就是在构造程序时避免出现不一致的锁顺序。可定时的与可轮询的的锁提供了另一种选择,避免死锁的发生。通过重新获取及释放锁来避免死锁。
- 可中断的锁获取操作。
- 非块结构的加锁。
- 性能考虑因素。经过优化,目前内置锁的性能与显示锁的性能已经差不了多少了,所以这点一般可以忽略,除非你要利用显示锁实现更加复杂的优化手段。
公平性对性能影响
大多数情况下,非公平锁的性能要高于公平锁的性能,原因是后者为了实现公平,会有更多的线程上下文切换成本。
当持有锁的时间较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。在这些情况下,允许“插队”带来的吞吐量提升(当锁处于可用的状态时,线程却还处于被唤醒的过程中)则可能不会出现。
在synchronized和ReentrantLock之间的选择
在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。
否则,还是应该优先使用synchronized.
读写锁
内在实现机制
- ReentrantReadWriteLock在读写锁都提供了可重入的加锁语义。
- 在构造时也可以指定是一个非公平的锁(默认)还是一个公平的锁。在公平的锁中,等待时间最长的的线程将优先获得锁。
- 读写锁之间的协调关系:如果这个锁由读线程持有,而另一个线程请求写入锁,那么其它读线程都不能获得读取锁,直到写线程使用完并释放了写入锁。显然的是,读取锁之间可以相互一起获得,写入锁则一定是独占的。
- 写线程可以降级为读线程,但读线程不允许升级为写线程(多个读线程都不会放弃自己的读取锁而导致死锁)。
适用场景
当锁的持有时间较长并且大部分操作都不会修改被守护的资源时,那么读-写锁能提高并发性。
如果写操作也很频繁,那可能独占锁更合适一些,因为写操作太多,竞争会很激烈,再加上协调读写锁,性能反而不如独占锁了。
还没有评论,来说两句吧...