码迷,mamicode.com
首页 > 其他好文 > 详细

第13章 显式锁

时间:2015-10-23 21:25:31      阅读:228      评论:0      收藏:0      [点我收藏+]

标签:

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

在激烈竞争的情况下,在非公平锁的性能高于公平锁的性能的一个原因是:在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。假设线程A持有一个锁,并且线程B请求这个锁。由于这个锁已被线程A持有,因此B将被挂起。当A释放锁时,B将被唤醒,因此会再次尝试获取锁。与此同时,如果C也请求这个锁,那么C很可能会在B被完全唤醒之前获得、使用以及释放这个锁。这样的情况是一种“双赢”的局面:B获得锁的时刻并没有推迟,C更早地获得了锁,并且吞吐量也获得了提高。

当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。在这些情况下,“插队”带来的吞吐量提升(当锁处于可用状态时,线程却还处于被唤醒的过程中)则可能不会出现。

ReentrantLock在加锁和内存上提供的语义与与内置锁相同,此外它还提供了一些其他功能,包括定时的锁等待、可中断的锁等待、公平性,以及实现非块结构的加锁。ReentrantLock在性能上似乎优于内置锁,其中在Java6中略有胜出,而在Java5.0中则是远远胜出。那么为什么不放弃synchronized,并在所有新的并发代码中都使用ReentrantLock?事实上有些作者已经建议这么做,将synchronized作为一种“遗留”结构,但这会将好事情变坏。

与显式锁相比,内置锁仍然具有很大的优势。内置锁为许多开发人员所熟悉,并且简洁紧凑,而且在许多现有的程序中都已经使用了内置锁——如果将这两种机制混合使用,那么不仅容易令人困惑,也容易发生错误。ReentrantLock的危险性比同步机制要高,如果忘记在finally块中调用unlock,那么虽然代码表面上能正常运行,但实际上已经埋下了一颗定时炸弹,并很有可能伤及其他代码。仅当内置锁不能满足需求时,才可以考虑使用ReentrantLock。

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

在读-写锁实现的加锁策略中,允许多个读操作同时进行,但每次只允许一个写操作。与Lock一样,ReadWriteLock可以采用多种不同的实现方式,这些方式在性能、调度保证、获取优先性、公平以及加锁语义等方面可能有所不同。

读-写锁是一种性能优化措施,在一些特定的情况下能实现更高的并发性。在实际情况中,对于在多处理器系统上被频繁读取的数据结构,读-写锁能够提高性能。而在其他情况下,读-写锁的性能比独占锁的性能要略差一些,这是因为它们的复杂性更高。如果要判断在某种情况下使用读-写锁是否会带来性能提升,最好对程序进行分析。由于ReadWriteLock使用Lock来实现锁的读-写部分,因此分析结果表明读-写锁没有提高性能,那么可以很容易地将读-写锁换为独占锁。

读取锁能否优先于其他正在等待的读线程和写线程而升级为一个写入锁?在大多数的读-写锁实现中并不支持升级,因为如果没有显式的升级操作,那么很容易造成死锁。(如果两个读线程试图同时升级为写入锁,那么二者都不会释放读取锁。)

ReentrantReadWriteLock为这两种锁都提供了可重入的加锁语义。与ReentrantLock类似,ReentrantReadWriteLock在构造时也可以选择是一个非公平的锁(默认)还是一个公平的锁。在公平的锁中,等待时间最长的线程将优先获得锁。如果这个锁由读线程持有,而另一个线程请求写入锁,那么其他读线程都不能获得读取锁,直到写线程使用完并且释放了写入锁。在非公平的锁中,线程获得访问许可的在唷是不确定的。写线程降级为读线程是可以的,但从读线程升级为写线程则是不可以的(这样做会导致死锁)。

第13章 显式锁

标签:

原文地址:http://www.cnblogs.com/a0000/p/4905639.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!