【问题标题】:Why does Java's SynchronizedMap use a single mutex and not a ReadWrite lock?为什么 Java 的 SynchronizedMap 使用单个互斥锁而不是 ReadWrite 锁?
【发布时间】:2017-09-04 12:37:39
【问题描述】:

通过调用Collections.synchronizedMap() 创建的Java 的SynchronizedMap 似乎对所有映射操作都使用了一个互斥锁。例如:

...

public V get(Object key) {
    synchronized (mutex) {return m.get(key);}
}

public V put(K key, V value) {
    synchronized (mutex) {return m.put(key, value);}
}

...

为什么作者选择对读取和写入操作使用单个互斥锁? ReadWriteLock 不是更适合这里吗?

【问题讨论】:

  • 该类自 java 5 以来已经过时(不确定是正式的还是实际的)。 ConcurrentMap 应该比这两种方法中的任何一种都更有效,因此没有理由花时间改进代码。但是是的,你是对的,它会更好。
  • 总的来说,我同意。但据我所知,JDK 中的 LinkedHashMap 没有直接的并发替代方案。这是一种无需外部库即可快速轻松地创建 LRU 缓存的方法。使其线程安全的唯一方法是使用锁。

标签: java multithreading dictionary locking synchronized


【解决方案1】:

我认为这样做的历史原因比什么都重要:如果你仔细看,ReadWriteLock 是在 JDK 1.5 版本中添加的,而 SynchronizedMap 从 1.2 开始就是 JDK 的一部分。

如果我们从行为兼容性的角度考虑,突然改变同步映射内部的工作方式(即通过同时读取和写入)是无效的,因为在 1.2 和1.5 将完全依赖于这种行为。另一点是 - Map 实现称为 synchronized,这实际上意味着它对每个访问进行排序,就像 synchronized 关键字所做的那样。

还有更多:读写锁有两种同步方式:公平和非公平。如果我们将简单的互斥锁替换为读写锁,我们会选择哪种风格?不同的锁实现呢?等等。

老实说,像 ConcurrentHashMap 那样提供通用并发映射更有意义,如果开发人员需要,其他一切都将由库编写者实现。

【讨论】:

  • 您能举一个例子,说明使用 rw 锁会导致事先不可能发生的行为吗?因为我不明白这怎么可能。考虑到没有充分的理由长时间使用该类,这更有可能是不值得改变行为。
  • (实际上很简单,我认为该行为是不可区分的:假设您有 n 个线程正在读取。使用新行为,这可以同时发生。使用旧行为,您可以中断每个线程在它解锁互斥体之后,但在它从方法返回之前。在所有线程都读取之后,让它们并发继续。外部可见的行为与新的相同。)
  • @Voo,不,读写锁是专门为允许多个并发读取和单个阻塞写入而设计的。同步地图,由于其设计,只允许单次读取写入。
  • 这不是重点。您必须展示一些在使用 RW 锁时可能发生的外部可观察行为,而这些行为在使用同步块时在任何情况下都不会发生。而且由于我概述的上述场景表明,在非常简单的调度下,您无法将多个并发读取与只有一个读取器的场景区分开来,我看不出这是怎么可能的。
  • 这是一个示例,说明清楚区分什么是 API 合同的一部分和什么是实现细节如此重要。合同只指定了外部可见的行为,它并不关心你如何实现这一点。而且性能通常不是 API 契约的一部分(除了为数据结构上的操作指定一些渐近复杂性之外)。
猜你喜欢
  • 1970-01-01
  • 2011-08-17
  • 2010-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-28
  • 1970-01-01
相关资源
最近更新 更多