Java 可以从一个线程转移到另一个线程的锁

Java 可以从一个线程转移到另一个线程的锁,java,concurrency,locking,atomic,semaphore,Java,Concurrency,Locking,Atomic,Semaphore,我正在寻找一种锁类型,其中持有锁的线程可以将它传递给它选择的另一个线程 以下是我想要它的原因: 我有一个类似于ConcurrentHashMap的类-一个被划分为多个段的专门集合 大多数修改只需要锁定一个段。有些需要锁定两个段(特别是修改关键点,使其从一个段移动到另一个段) 大多数读取不需要锁定-一个易失性读取通常就足够了,但如果修改计数检查失败,偶尔搜索需要一次锁定所有段 搜索在多个线程中完成(通过ThreadPoolExecutor) 搜索功能必须具有所有段的一致视图(例如,在从一个段移动

我正在寻找一种锁类型,其中持有锁的线程可以将它传递给它选择的另一个线程

以下是我想要它的原因:

  • 我有一个类似于
    ConcurrentHashMap
    的类-一个被划分为多个段的专门集合
  • 大多数修改只需要锁定一个段。有些需要锁定两个段(特别是修改关键点,使其从一个段移动到另一个段)
  • 大多数读取不需要锁定-一个易失性读取通常就足够了,但如果修改计数检查失败,偶尔搜索需要一次锁定所有段
  • 搜索在多个线程中完成(通过
    ThreadPoolExecutor
  • 搜索功能必须具有所有段的一致视图(例如,在从一个段移动到另一个段时,不得遗漏条目)
  • 搜索任务(针对单个段)可能随时中断
现在我正在考虑在主线程中调用搜索方法的情况,发现它需要锁定所有段。所有的段锁必须由主线程同时持有(以确保没有干扰),但不是由主线程执行更新,而是工作线程之一。因此,一旦主线程知道它有一个一致的快照,我就试图让它“传递”锁

我们过去对整个集合使用一个锁,但随着它变得越来越大,对于小的更新,会有太多的争用和不可接受的高延迟

解锁和重新锁定(在重入锁定上)是不安全的-另一个线程可能会在工作线程开始搜索之前修改段

普通的
信号灯
可以处理不同线程的锁定和解锁。随后出现的问题是谁应该释放信号量-工作线程需要一种方法来表示它已经拥有锁的所有权(因为它可能在这一点之前或之后抛出异常,并且主线程需要知道是否要清理)单元测试也很棘手,因为您永远不知道信号量的获取或释放是否发生在正确的线程中

如果锁可以在其他方法中以可重入方式使用(不在线程之间传递),这将是一个额外的好处


我想需要一个
信号量
和一个
原子布尔
原子参考
的组合,但是快速的谷歌搜索并没有显示任何例子。有什么理由不应该使用这种方法吗?

反向使用读/写锁可能有效。在这种习惯用法中,读锁持有者执行对数据结构的并发写入(例如,数组中的独立插槽)。写锁用于获取独占访问以执行一致读取(例如,对数组求和)。这是一个很少使用的习语,但在那些古怪的情况下却很优雅。你的问题有点难以解决,但这至少可以为具体的解决方案提供一些灵感。我怀疑,如果理解得更深入,这个问题可以简化,这样经典的解决方案就更合适了

并发最适合于简单模型。考虑到您描述的长度和模型的复杂性,我建议您尝试简化您的需求。我认为它更有可能高效地工作。我所知道的唯一唤醒特定线程的方法是Unsafe.park()和Unsafe.unpark(线程),但我不建议直接使用这些方法。如果您只需要一个可以传递的“锁”,为什么不使用令牌传递机制,将每个段锁作为令牌,并将其逐字传递?如果集合搜索时间足够长,此令牌甚至可以是一个文件,该文件充当锁。主线程可以简单地将“锁”交给工作线程。是否可以通过使用具有关联“更新”的持久结构创建新结构而不是就地修改来以无锁方式对此进行建模?您描述的是一个解决方案,而不是您正在解决的根本问题,因此很难提出一个解决方案。正如我所说,阅读
ConcurrentHashMap
的源代码可能会更清楚。我的类非常类似,只是当它执行与
包含(对象)
等效的操作时,它会搜索(可能)不同线程中的每个段。我会看看我是否可以调整这个问题使它更清楚。我认为你的解决方案会奏效。我理解技术方面,但不理解行为方面。为什么要移动钥匙?为什么必须搜索(例如,为什么不使用ConcurrentSkipListMap)?是什么契约使它不同于普通映射?键没有单一的自然顺序。此搜索操作进行“模糊”比较,必须检查段中的每个元素,以查找最近的几个匹配项。修改密钥有时会更改其哈希代码,使其属于不同的段。我必须以原子方式移动它,以确保它在移动时不会被遗漏在搜索之外。好吧,那么版本控制是解决这个问题的经典方法。这是通过使用持久数据结构或复制副本来完成的,其中事务(放置/删除/移动)被重放(例如,更新提交到写入队列)。后者可以通过使用读写器锁来支持并发搜索,在读写器锁中,当有待重播的更新时,将获取写入器锁。这两种方法都允许搜索不阻止其他写入程序,而反向读/写锁将阻止其他写入程序(但可以进行检查并使其最小化)。