并发王者课 - 铂金 3:一劳永逸 - 如何理解锁的多次可重入问题
欢迎来到《并发王者课》,本文是该系列文章中的第 16 篇。
在前面的文章《铂金 1:探本溯源-为何说 Lock 接口是 Java 中锁的基础》中,我们提到了锁的可重入问题,并作了简单介绍。鉴于锁的可重入是一个重要概念,所以本文把拿出来做一次单独讲解,以帮助你彻底理解它。
一、锁的可重入所造成的问题
首先,我们通过一段示例代码看锁的可重入是如何导致问题发生,以理解它的重要性。
在上面这段代码中,我们创建了一片野区,包含了野区 A 和野区 B。接着,我们再创建一个打野英雄铠,让他进去野区打野,看看会发生什么事情。
输出结果如下:
从结果中可以看到,虽然在同一块野区,但是铠只进入野区 A,却没能进入野区 B,被阻塞在半道上了。从代码分析上看,野区的两个方法都声明了synchronized
,但铠在进入野区 A 之后,野区进行了锁定isAreaLocked = true
,导致铠进入野区 B 时失败。
这就是典型的锁的可重入所造成的问题。在并发编程时,如果未能处理好这一问题,将会造成线程的无限阻塞,其后果和死锁相当。
二、理解锁的可重入
所谓锁的可重入,指的是锁可以被线程 重复 或 递归 调用,也可以理解为对同一把锁的重复获取。 如果未能处理好锁的可重入问题,将会导致和死锁类似的问题。
三、如何避免锁的可重入问题
避免锁的可重入问题,需要注意两个方面:
尽量避免编写需要重入获取锁的代码;
如果需要,使用可重入锁。
在 Java 中,synchronized
是可以重入的,下面的这段代码在调用时不会产生重入问题。
但是,基于 Lock 接口所实现的各种锁并不总是支持可重入的。在前面的文章中,我们已经展示过不支持重入的 Lock 接口实现。在具体的场景中使用时,需要务必注意这点。如果需要可重入锁,可以使用 Java 中的 ReentrantLock 类。
小结
在本文中,我们再次介绍了锁的可重入问题,并介绍了其产生的原因及避免方式。Java 中的synchronized
关键字支持锁的可重入,但是其他显示锁并非总是支持这一特性,在使用时需要注意。
此外,需要注意的是,锁的可重入对锁的性能有一定的影响,而且实现起来更为复杂。所以,我们不能说锁的可重入与不可重入哪个好,这要取决于具体的问题。
正文到此结束,恭喜你又上了一颗星✨
夫子的试炼
查看 ReentrantLock 源码,了解其支持可重入的原理。
延伸阅读与参考资料
关于作者
关注公众号【庸人技术笑谈】,获取及时文章更新。记录平凡人的技术故事,分享有品质(尽量)的技术文章,偶尔也聊聊生活和理想。不贩卖焦虑,不做标题党。
如果本文对你有帮助,欢迎点赞、关注、监督,我们一起从青铜到王者。
版权声明: 本文为 InfoQ 作者【秦二爷】的原创文章。
原文链接:【http://xie.infoq.cn/article/5ec6808bea3c3d71145c3e4e2】。文章转载请联系作者。
评论