Java并发编程实战读书笔记——第十三章 显式锁

Synchronized,volatile,ReentrantLock

ReentrantLock不是一种替代内置加锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。

13.1 Lock与ReentrantLock

Lock接口提供了一种无条件、可轮询的、定时的以及可中断的显示锁获取操作。

这里写图片描述

ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。与synchronized相比,ReentrantLock还提供了可重入锁的加锁语义,它还为处理锁的不可用性提供了更高的灵活性。

为什么要创建与内置锁如此相似的新加锁机制?

  • 无法中断一个正在等待获取锁的线程
  • 无法在请求获取一个锁时无限地等待下去
  • 无法实现非阻塞结构的加锁规则

Lock接口的标准使用形式,必须在finally块中释放锁。

13.1.1 轮询锁与定时锁

可定时的与可轮询的锁获取模式是由tryLock方法实现地的,与无条件的锁获取模式相比它具有更完善的错误恢复机制,可避免死锁的发生。

如果不能获得所有需要的锁,那么可以使用可定时的或可轮询的锁获取方式,从而使你重新获得控制权,它会释放已经获得的锁,然后重新尝试获取所有锁。

这里写图片描述

这里写图片描述

13.1.2 可中断的锁获取操作

定时的锁获取操作能在带有时间限制的操作中使用独占锁,可中断的锁获取操作同样能在可取消的操作中使用加锁。lockInterruptibly

这里写图片描述

13.1.3 非块结构的加锁

内置锁的获取和释放都是基于代码块的,释放锁的操作总是与获取锁的操作处于同一个代码块,而不考虑控制权如何退出代码块。自动的锁释放操作简化了对程序的分析,避免了可能的编码错误。

13.2 性能考虑因素

竞争性能是可伸缩性的关键要素。锁的实现方式越好,将需要越少的系统调用和上下文切换,并且在共享内存总线上的内存同步通信量也越少。

在JAVA1.6中ReentrantLock与内置锁的性能相当,稍差一点。

性能是一个不断变化 的指标,如果在昨天的测试基准中发现X比Y更快,那么今天可能已经过时了。

13.3 公平性

ReentrantLock的构造函数中提供了两种公平性选择:创建一个非公平的锁或者一个公平的锁。在公平的锁上,线程将按照它们发出请求的顺序来获取锁,但在非公平的锁上,刚允许插队:当一个线程请求非公平的锁时,如果在发出请求时该锁的状态变为可用,那么这个线程将路过队列中所有的等待线程并获得这个锁。

统计上的公平性保证:确保被阻塞的线程能最终获得锁。

非公平锁的性能要高于公平锁的性能。

这里写图片描述

非公平锁的性能高于公平锁的一个原因是:在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。假设A持有锁,B请求锁,当A释放锁,B将被唤醒,因此会再次请求锁。此时,如果C请求锁,那么C可能会在B完全唤醒之前获得、使用以及释放这个锁。双赢:B没有推迟获得锁的时刻,C更早的获得了锁,提高了吞吐量。

当持有锁的时间相对较入,或者请求锁的平均时间间隔较长,那么应该主使用公平锁。

13.4 在synchronized和ReentrantLock之间进行选择

ReentrantLock在加锁和内存上提供的语义与内置锁相同,此外它提供了定时锁、轮询锁、可中断锁、公平性,以及实现非块结构的加锁。

内置锁有很磊的优势,更熟悉,简洁紧凑,安全。

在一些内置锁无法满足需求的情况下,ReentrantLock可以作为一种高级工具。当需要一些高级功能时才应该使用ReentrantLock,这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非结构的锁。否则应该优先使用synchronzied。

ReentrantLock的非块结构特性意味着,获取锁的操作不能与特定的栈帧关联起来,而内置锁却可以。

未来更可能会提升synchronzed而不是ReentrantLock的性能。因为synchronized是JVM的内置发性,它能执行一些优化,例如对线程封闭的锁对象的锁消除优化,通过增加锁的粒度来消除内置的同步,通过类库来实现的可能性不大。否则就性能方面来说,应该选择synchronized而不是ReentrantLock。

13.5 读-写锁

一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。

这里写图片描述

对于在多处理器系统上被频繁读取的数据结构,读写锁能够提高性能,而在其它情况下,比独占锁性能要稍差一些,因为复杂度高。

ReentrantReadWriteLock为这两种锁都提供了可重入的加锁语义。提供公平与非公平的构造函数。在公平的锁中,等待时间最长的线程将优先获得锁。如果这个锁由读线程持有,而另一个线程请求写入锁,那么其他读线程都不能获得读取锁,直到写线程使用完毕并且释放了写入锁。在非公平的锁中,线程获得锁的顺序是不确定的。写线程降级为读线程是可以的,但从读线程升级为写线程刚是不可以的,会导致死锁。

这里写图片描述

小结

与内置锁相比,显式的Lock提供了一此扩展功能,在处理锁的不可用性方面有着更高的灵活性,并且对队列有着更好的控制。但ReentrantLock不能完全替代synchronized,只有在synchronized无法满足需求时,才能使用它。

读写锁允许多个读写线程并发地访问被保护的对,当访问读取操作为主的数据结构时,它能提高程序的可伸缩性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值