【问题标题】:Why acquire a (reentrant) lock recursively?为什么要递归获取(可重入)锁?
【发布时间】:2023-07-01 02:10:01
【问题描述】:

ReentrantLock 允许线程递归地获取相同的锁,因此锁计数在连续锁定/解锁时递增和递减。而锁计数在释放给其他线程之前必须减为零。

为什么或在什么情况下我会编写代码以递归方式获取锁?

我能看到的唯一一点就是让我们可以方便地编写递归代码,递归调用一个方法(在其执行过程中获取锁)。

在其他情况下,线程递归/重复获取锁可能有用吗?

澄清问题:

  • 请忽略可重入的锁。恰好递归是由可重入锁提供的。
  • 我指的是锁的递归特性
  • 请不要回答为什么要使用可重入锁。
  • 请不要回答“递归不是可重入锁的主要特征”
  • 我想知道什么情况下需要递归获取锁,不管锁是否可重入。

【问题讨论】:

    标签: java multithreading recursion reentrantlock


    【解决方案1】:

    不妨更好地搜索: this should be helpful

    重入锁用例:

    一个可重入锁应用程序的(有点通用和人为的)示例可能是:

    1. 您有一些计算涉及一个遍历 图(可能有循环)。遍历可能会访问相同的 节点不止一次由于循环或由于多个路径到 同一个节点。

    2. 数据结构是并发访问的,可以 出于某种原因更新,也许是由另一个线程。你需要成为 能够锁定单个节点以处理潜在的数据损坏 由于比赛条件。出于某种原因(也许是性能)你 不想全局锁定整个数据结构。

    3. 您的计算无法保留有关哪些节点的完整信息 您访问过,或者您使用的数据结构不允许 “我以前来过这里吗”的问题需要快速回答。

    4. 这种情况的一个例子是一个简单的实现 具有优先级队列的 Dijkstra 算法以二进制形式实现 使用简单链表作为队列的堆或广度优先搜索。 在这些情况下,扫描队列以查找现有插入是 O(N) 而且您可能不想在每次迭代时都这样做。

    在这种情况下,请跟踪您已经锁定了哪些锁 收购是昂贵的。假设您想在节点上进行锁定 水平重入锁定机制减轻了告诉的需要 您之前是否访问过某个节点。你可以盲目地锁定 节点,可能在您将其从队列中弹出后将其解锁。

    【讨论】:

    • 抱歉编辑您的答案,因为我需要通过编号来引用这些项目。
    • 第 1 项:如果遍历必须是原子的,那么用一个锁锁定整个树/图不是更好吗?通过单独锁定节点以原子方式遍历树会不会带来非常可怕的死锁风险?
    • 第二项:当我需要获取三个节点来完成一个原子操作时,我会先获取这些节点。如果我未能获得完整的节点集,我将退出并释放所有节点。如果它达到我无法跟踪我需要以原子方式获取的节点的情况,由于算法的复杂性,我可能会获取它两次 - 该算法应该被禁止,除非它是由超人、奥特曼或美国队长编写的。
    • 第四项:Dijkstra的算法需要加锁吗?
    • 我最近的一个项目,我们设计了有粒度的图表,这样我们就可以按区域锁定图表。我们只是锁定了图的整个区域。这样我们最多锁定了三个区域,没有两次获得相同锁定的风险。正确设计图比随意使用优雅的容易死锁的锁定算法更安全。