可重入锁 RenentrantLock 是必不可少的吗?

2021-09-18 14:36:32 +08:00
 RedBlackTree
递归调用场景下,可以直接把锁提到最外面来避免死锁。
我能想到的另一个重入锁的场景是,一个函数需要加锁但无法保证调用者是否已经占有锁,可能占有了锁,也可能没有占有锁,不可重入锁则函数需要提供两个变种,重入锁只需要提供一个就行了。
Go 的 Sync.Mutex 是不可重入的,而且也没有提供可重入的锁。Java 中,可重入锁似乎是必须的,Java Concurrency in practive 书中讲可重入性时举了这么个例子:
https://imgur.com/yDpZTfH
在我看来,这是继承带来的耦合导致不得不使用重入锁来解决这个问题。
我感觉可重入锁并不是必不可少的,请大家指点一下。
2099 次点击
所在节点    程序员
7 条回复
RedBlackTree
2021-09-18 14:43:27 +08:00
怎么图片解析不出来?
monetto
2021-09-18 14:52:49 +08:00
RenentrantLock 只是没东西翻译了,翻译成了一个可重入锁,其实我觉得这玩意儿翻译成,“基于 AQS 的 公平 /非公平 且 可重入的锁”,像 synchronized 也是可重入的,只不过实现原理不一样。

RenentrantLock 的原理是判断 AQS 的队列中,当前线程是否为链表头,那么就可以直接获取,不需要在 AQS 中排队。

“递归调用场景下,可以直接把锁提到最外面来避免死锁”,这个形容不是很准确,有很多情况是,非递归调用,但是多线程情况下函数 A 和函数 B 的执行都需要这个独占资源,那么这时候就需要使用可重入锁。

严格的说,为了解决的问题是 “某个线程,再次获取想要独占的资源”而出现的,这种情况在多线程编程很多时候是会出现的,这时候就需要可重入锁解决问题,不是耦合导致不得不重入。

Go 是如何解决这种问题的我并不清楚,但是感觉原理应该和 JVM 差不多。
chendy
2021-09-18 14:54:04 +08:00
但是 java 里貌似也没有“不可重入”的锁
A 和 B 俩方法用同一把锁,A 内部会调用 B,如果锁不可重入就直接卡死了
BBCCBB
2021-09-18 15:18:55 +08:00
这个可以用 我可以不用, 但你不能没有 来解释.
RedBlackTree
2021-09-18 15:30:25 +08:00
@chendy 这种情况把方法内部逻辑拆出来,提供两个入口,一个默认未占有锁一个默认已占有锁也可以吧
Brentwans
2021-09-18 17:10:32 +08:00
重入锁的单位是线程,个人觉得这里的重入的特性,不算是必不可少的。
Shaw314
2021-09-18 18:51:50 +08:00
@chendy 线程池 ThreadPoolExecutor 里面的 Worker 类就通过 AQS 实现了一个不可重入的锁。 不过其实也不能算是锁,作用并不是用来控制并发的,主要是为了能标识这个 worker 是不是在工作,方便打断。https://github.com/daiqingliang/java_jdk1.8.0_111/blob/master/java/util/concurrent/ThreadPoolExecutor.java#L629

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/802765

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX