图3. Relative throughput for synchronization, barging Lock, and fair Lock, with four CPUs
图 4. Relative throughput for synchronization, barging Lock, and fair Lock, with single CPU
ReentrantLock这么完美吗?
看起来ReentrantLock在各个方面都比同步更好,同步能做的它都可以做(具有相同的内存和并发语义),同步不能做的它也可以做,并且在负载下具有更好的性能。
那么,我们真的应该放弃synchronized吗?还是等着之后被改进,或者甚至直接用ReentrantLock重写我们现有的synchronized代码?
实际上,有关Java编程的书籍,在多线程的章节中都采用了这种方法,将示例完全用Lock实现,对synchronized是一带而过。我认为这是一件好事。
尽管ReentrantLock的表现是令人印象深刻的,在同步方面有很多明显的优势,但是我认为急于将synchronized视为不推荐的功能是不明智的。
java.util.concurrent.lock中的锁类是针对高级开发者或者高级组件的。通常,除非你需要Lock的高级功能,或者你有证据(不仅是怀疑)证明使用synchronized遇到了性能瓶颈,否则你都应该坚持使用synchronized。
为什么在面对“更好”的Lock时,我显着有点保守主义?
其实与Lock相比,synchronized仍然具有一些优势。
其一,使用synchronized时你不可能忘记释放锁,因为在退出同步块时JVM帮你做了。
而在finally块中释放锁是很容易忘记的,这对程序的伤害极大。而且一旦出现问题,你很难定位到问题在哪。(这是完全不推荐初级开发人员不使用Lock的充分理由)。
另一个原因是,当JVM使用synchronized获取锁和释放锁时,会保留锁相关的信息,在进行线程dump时这些信息会包括在内。这对程序调试是无价的,因为它可以让你定位到死锁或者其他异常的根源。而Lock只是一个普通的类,JVM也不知道某个线程持有哪个Lock对象。
那么,什么时候应该使用ReentrantLock?答案很简单,在你需要使用synchronized无法提供的功能时,例如定时锁、可中断锁、非块结构锁、多个条件变量、或锁轮询。
ReentrantLock还具有很好的可伸缩性,如果你确实遇到锁竞争激烈的情况,就使用它吧。
不过请记住,绝大多数synchronized块几乎从未遇过任何竞争,更不用说激烈的竞争了。
我建议你先使用synchronized,直到被证明synchronized不能满足需求了,
而不是简单的假设“ReentrantLock的性能会更好”。请记住,Lock是面向高级用户的高级工具。
真正的高手倾向于使用最简单的工具完成工作,直到他们确信简单工具已经不适合了。
一个永恒的真理,先使其正确,然后再考虑是否需要让它更快。
Lock框架是synchronized的兼容替代品,它提供了许多同步未提供的功能,以及在竞争条件下更好的性能。
但是,这些优势还不足以让你总是优先选择ReentrantLock,而冷落了synchronized。
根据你的实际需求来决定是否需要ReentrantLock的魔法力量。
我想,在大多数情况下你是不需要的Lock的,因为同步可以很好的胜任,可以在所有JVM上工作,可以被更广泛的开发者理解,并且不容易出错。
文中作者多次强调了(感觉有点啰嗦,滑稽脸),非必要情况下不要使用ReentrantLock,而是优先考虑synchronized。
之所以对ReentrantLock这么保守,我想是因为这篇文章写在JDK5刚刚发布的2004年,那时候Lock对于大多数开发者还是一个陌生的东西,对其工作原理和优缺点都不太熟悉。
但是2020年的今天,JDK13都发布了,JDK8已成了主流,那么想用就用吧。
1.non-block-structured locking的说明:https://stackoverflow.com/questions/59677725/what-does-non-block-structured-locking-mean)
2.英文版原文 : https://www.ibm.com/developerworks/java/library/j-jtp10264/index.html?ca=drs-