【问题标题】:Why do Java's synchronized Collections not use Read/Write locks?为什么 Java 的同步集合不使用读/写锁?
【发布时间】:2012-07-24 19:46:17
【问题描述】:

在不鼓励使用 HashtableVector 之类的东西之后,当 Collections 同步包装器出现时,我认为同步处理会更有效。现在我查看了代码,我很惊讶它实际上只是用同步块包装集合。

为什么 ReadWriteLocks 不包含在 Collections 中的 SynchronizedMap 中?是否存在一些不值得的效率考虑?

【问题讨论】:

  • 询问设计师。你只会在这里得到猜测。我的猜测是集合是在 1998 年左右发布的,而 ReadLocks 等则是几年后发布的。

标签: java collections concurrency


【解决方案1】:

读写锁是性能优化的一部分,这意味着它可以在某些情况允许更大的并发性。必要条件是,它们应用于大部分时间都被读取,但没有被修改的数据结构。

在其他条件下,它们的性能比独占锁稍差,这很自然,因为它们具有更大的复杂性。

如果读写锁的锁通常保持相当长的时间并且只对受保护的资源进行少量修改,则效率最高。

因此,读写锁是否优于独占锁取决于用例。最终,您必须通过分析来衡量哪些锁性能更好。

考虑到这一点,为Collections.synchronizedMap 选择一个排他锁似乎是合适的,它解决了一般用例而不是大多数读者的特殊情况。

更多链接

[...] 但是,在写入操作更多的配置中 流行,具有同步块的版本比 50% 快 一种基于 Sun 1.6.0_07 JVM 的读写锁(快 14% 使用 Sun 1.5.0_15 JVM)。

在只有 1 个的低争用情况下 reader 和 1 writer,性能差异不那么极端, 并且三种类型的锁中的每一种都产生了最快的版本 至少一台机器/VM 配置(例如,请注意 ReentrantLocks 在装有 Sun 1.5.0_15 JVM 的 2 核机器上最快)。

【讨论】:

  • 这不能回答问题。例如,他在问为什么在SynchronizedMap 中没有使用它们。
  • 谢谢@platzhirsch 我必须说,但是,我并不完全相信ReadWriteLock 比标准同步块的成本要高得多,而且优势似乎很大。如果你还有一分钟,你能详细说明一下成本差异吗?谢谢!
  • @Miquel 我同意,它的成本并不高,正如我所说的那样,它的性能只会稍差一些。但这只是我在理论上所知道的,虽然我在处理这些主题时遇到了一些有趣的基准,但我尝试在我的答案中添加一些以给出成本差异的具体数字。
  • @platzhirsch 非常感谢,您添加的数据真的让我感到惊讶。与知道如何支持他们的论点的人讨论真是太高兴了!
  • 但是我最担心的事情是,这将成为那些试图推出自己的锁定机制的人的素材。对于 99% 的问题空间,建议使用 synchronized,因为您不必主动清除锁定。很多人都在进行过早的优化,或者正在优化他们应用程序的错误部分。但话虽如此,但要牢记这一点。
【解决方案2】:

我不认为使用ReadWriteLock(如果这就是你所说的)比使用synchronized 关键字更快。这两种构造都施加了锁和竖立内存屏障以及“发生在之前”的限制。

您可能正在谈论在Collections.synchronizedMap(...) 和朋友中做一些聪明的事情,其中​​读取方法被读取锁定,写入方法写入锁定以提高性能。这可能适用于 java.util 集合类,但如果正在计算 get() 方法或其他东西,则可能会导致用户实现的 Maps 出现同步问题 - 即实际上是“只读”的方法更新集合。是的,这样做是个糟糕的主意。

ConcurrentHashmap 被编写为高性能,直接使用volatile 字段而不是synchronized 块。与Collections.synchronizedMap(...) 相比,这使得代码更加复杂,但速度也更快。这就是为什么建议在Collections.synchronizedMap(new HashMap<...>()) 以上的高性能情况下使用它的原因。

【讨论】:

  • 天啊。感谢您的解释。那么您对 ​​Hashtable 被劝阻的整个想法持何立场,为什么 ConcurrentHashmap “遵守与 Hashtable 相同的功能规范”?或者为什么要创建 ConcurrentHashmap 而不是更新 Collections.synchronizedMap()
  • HashTable 是在编写任何 Java 1.5 并发类和集合之前编写的。它使用方法级别的synchronized 语句与使用内部互斥锁的synchronizedMap(...)。它的效率也低于HashMap。这只是旧代码。
  • 正如我的回答所暗示的@Miquel,ConcurrentHashMap 是从头开始编写的(顾名思义)是一个高性能并发哈希映射。它的性能比synchronizedMap(...) 高得多,因为锁的粒度要细得多。它也是很多更复杂的,并且有一些方法调用(例如ConcurrentMap 方法)添加了一些有用的并发功能。
【解决方案3】:

除以下内容外,大多数推理都已解决。 SynchronizedMap/Set/List 以及 Hashtable 和 Vector 依赖于正在同步的集合实例。因此,许多开发人员使用这种同步来确保原子性。例如。

List syncList = Collections.synchronizedList(new ArrayList());

//put if absent
synchronized(syncList){
   if(!syncList.contains(someObject)){
     syncList.add(someObject);
   }
}

这对于所有操作都是线程安全和原子的,因为 synchronizedList 将自行同步(即添加、删除、获取)。这就是为什么 Hashtable 类没有被改造以支持类似于 ConcurrentHashMap 的锁条带化的主要原因。

因此,对这些集合使用 ReadWriteLock 将失去原子操作的能力,除非您能够获取锁实例。

【讨论】:

  • 对不起,我不能跟着你。为什么你不能完全按照你的代码 sn-p 而使用 Hashtable 呢? (假设您在示例中选择了 synchronizedMap 而不是 synchronizedList)
  • 嗯,你可以,但我的回答解决了为什么你不能使用 ReadWriteLock 进行 put 如果不存在,除非你能够在操作之前获取写锁。我对 RWL 支持的集合的假设是锁是隐藏的,您无法控制任何额外的原子操作。
  • 现在我知道了。感谢您的澄清!
【解决方案4】:

这是因为这些类早于读/写锁,后者进入 Java 相当晚(Java 5)。无论如何,由于它们的硬编码细粒度锁定,它们很少有用。

【讨论】:

  • 如果他说的是 ReadWriteLock,那是根据 javadocs 在 1.5 中引入的。
  • 谢谢大家。所以,是的,它应该使用它们,但不,它不是因为它们当时不可用。很公平。虽然很惭愧!谢谢!
  • 无论如何,线程协调是您应该始终自己做的事情。同步集合所能做的最好的事情是非常、非常适度的,并且仅限于几个实际用例。
【解决方案5】:

这与效率无关。在Collections.synchronizedMap 中使用synchronized 并没有错。如果使用 ReadWriteLock 来实现 Map,我想称之为Collections.LockedMap ;)

认真点,Collections.synchronizedMap 是在 Lock 出现之前几年写的。这是一个发布后无法更改的 API。

【讨论】:

  • 同意。直到@platzhirsch 提出了一些性能指标,并且 Gray 提到了锁暴露问题,我并不清楚 API 是否必须改变,只是内部实现。
【解决方案6】:

要添加到@JohnVint's answer,请考虑迭代问题。 documentation 明确要求客户端同步:

用户必须手动同步返回的 迭代时列出:

List list = Collections.synchronizedList(new ArrayList());
    ...
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext())
        foo(i.next());
}

不遵循此建议可能会导致不确定的行为。

这只是因为客户端可以在返回的地图上共享内在锁。如果要在内部使用读/写锁,则返回的接口必须支持某种方式,至少可以访问读锁以实现安全迭代。这将使 API 变得复杂,从而产生可疑的好处(正如其他人所解释的那样)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-08
    相关资源
    最近更新 更多