【问题标题】:Multithreaded deletion from a BST with only local locks仅使用本地锁从 BST 中进行多线程删除
【发布时间】:2019-01-05 23:59:26
【问题描述】:

在用 Java 编写 BST 的多线程实现时,我遇到了以下问题。此 BST 不应该使用全局锁,而是尽可能少地锁定,特别是只锁定正在更改的节点(添加和删除命令)。因此,只要不尝试更改与您相同的节点,其他线程就可以在树中处于活动状态。 我找不到实现删除具有 2 个子节点的节点的方法。正常算法说找到要删除的节点的顺序后继,将其放置在已删除节点的位置,然后删除复制的节点。但这可能会给位于这两个节点之间并需要复制节点的线程带来问题,在传输之后,该线程将找不到请求的节点,即使它在树中。 看下面的例子:Tread 1 正在执行remove(5)。它找到下一个键 (6) 并将其复制到节点,然后从副本中删除该节点。但同时另一个线程正在执行contains(6) 命令并在节点 8 上等待,执行后节点 6 将不再在其路径中,即使 6 节点仍在树中,它也会返回错误结果.

remove 命令之前的状态图示(蓝色箭头表示第 2nd 线程所在的位置。

remove 命令后的状态图示(蓝色箭头表示第 2nd 线程所在的位置。

如何在不锁定整个树的情况下解决这个问题?

【问题讨论】:

  • 你为什么要使用 BST 来满足这个要求?
  • @RohitShetty,这是一个专门用于创建多线程安全 BST 的练习。
  • 我猜stackoverflow.com/a/54065194/4109972 是一个选项

标签: java multithreading binary-search-tree synchronized


【解决方案1】:

我使用的解决方案是为 BST 设置一个版本号,每当一个有两个子节点的节点需要 remove 时,我都会在删除重复的节点之前增加版本号。

然后,每个操作在开始之前都会检查版本号,如果我得到指示未找到密钥的结果,我会检查版本号是否仍然相同,如果不相同,我会重试该操作。

这意味着:

  • 对于remove 和contains - 如果操作失败(意味着找不到密钥)并且版本已更改,请重试。
  • 对于insert - 我检查版本号,不是在操作结束时,而是在我处于叶子节点时以及在创建和添加新节点之前。如果我要添加一个新节点,这意味着我没有找到具有该键的节点,我想在更改它并创建一个新叶子之前确保该键确实不在树中以防止出现以下情况一个双键将被添加到树中,然后我需要通过删除节点来重做。

【讨论】:

  • 但这不会要求您遵守对包含的锁定吗?我知道这将如何工作,但我不知道这是否是最佳实现。
  • @RohitShetty,包含运行而不使用任何锁,因为它不会写入或更改树中的任何内容。唯一需要同步的是版本计数器,但我使用原子操作(Java 的AtomicInteger)。
【解决方案2】:

独立于 BST 同步节点。这意味着如果您要删除一个节点,您将需要锁定受影响的最顶层节点及其下方的所有节点,以防止其他线程读取或修改这些节点。不幸的是,对于 BST,这可能意味着根据操作锁定根节点,这将有效地防止任何读取发生。

我相信这将是您尝试做的最快的实现。

如果排序无关紧要,我建议改用 HashMap 实现。您只需要锁定存储桶而不是锁定整个地图。

【讨论】:

    【解决方案3】:

    我认为用哈希集支持 BST 可能是这里的方法。这样,您的查找将独立于 BST 锁定,并会在包含时为您提供 O(1)。

    【讨论】:

      猜你喜欢
      • 2012-09-07
      • 2013-05-27
      • 2023-03-12
      • 2015-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-10
      • 1970-01-01
      相关资源
      最近更新 更多