【问题标题】:ConcurrentHashMap, which concurrent features improved in JDK8ConcurrentHashMap,JDK8中改进了哪些并发特性
【发布时间】:2016-08-02 23:36:00
【问题描述】:

哪位并发高手能解释一下ConcurrentHashMap,哪些并发特性相比之前的JDK有所改进

【问题讨论】:

    标签: concurrency java-8 concurrenthashmap


    【解决方案1】:

    我认为与JDK7相比有几个变化:

    • 延迟初始化:在JDK8中,每个segment使用的内存只有在map中添加了一些实体时才会被分配。在 JDK7 中,这是在创建地图时完成的。
    • JDK8 中增加了一些新功能,如 forEach、reduce、search 等。
    • 内部结构变化:jdk8中使用TreeBin(红黑树)提高搜索效率。

    【讨论】:

      【解决方案2】:

      嗯,ConcurrentHashMap 已完全重写。在 Java 8 之前,每个 ConcurrentHashMap 都有一个“并发级别”,该级别在构建时固定。出于兼容性原因,仍然有一个constructor accepting such a level,尽管没有以原始方式使用它。该映射被分成与其并发级别一样多的段,每个段都有自己的锁,因此理论上,如果它们都针对不同的目标,则可以有高达 并发级别 的并发更新段,这取决于散列。

      在 Java 8 中,每个哈希桶都可以单独更新,因此只要没有哈希冲突,就可以有与其当前容量一样多的并发更新。这与保证原子更新的compute 方法等新功能一致,因此,至少锁定了更新的哈希桶。在最好的情况下,它们确实只锁定了一个桶。

      此外,ConcurrentHashMap 受益于适用于所有类型哈希映射的一般哈希改进。当某个桶发生哈希冲突时,该实现将求助于该桶内的排序映射结构,因此在搜索桶时会降低到O(log(n)) 复杂度,而不是旧实现的O(n) 复杂度。

      【讨论】:

      • 嗨 Holger,** 在 Java 8 中,每个哈希桶都可以单独更新** 根据您的回答,在 Java 8 中,它在更新时只锁定单个桶,而不是一个段(集合桶)。但这与旧 Java 版本相同,我们可以通过传递等于当前容量的并发级别来实现这一点。这意味着我们可以为 16 个存储桶指定 16 个线程。那么它有什么不同呢?我知道我的理解有一些差距。请纠正我。
      • @Pradeep Singh:不完全是。问题在于它取决于实际密钥的哈希码,映射将存储在哪个段中。无法保证在具有 n 个段的映射中存储 n 个键最终每个段只有一个键,事实上,越高的 n完美分布的可能性越小。此外,容量不是恒定的,但段数是恒定的。因此,如果您使用反映地图可能拥有的最高容量的段数来初始化 Java 7 ConcurrentHashMap,您最终将获得巨大的开销,可能从未使用过。
      • @Holger,在 java 7 或更早版本中使用段有什么好处?似乎从一开始就应该使用这种设计(没有段的哈希桶)。
      • @aniliitb10 好吧,这就是你可以说的许多新发明。一旦他们在那里,他们看起来如此明显,以至于任何人都会问“为什么没有人早点考虑到这一点?”。为什么在所有这些“替代散列”实验之前,他们没有用二叉树来解决桶冲突的想法?为什么直到 Java 9 才意识到,大多数字符串仍然适合 iso-latin-1,所以强制对所有字符串进行两字节编码是一种巨大的浪费?
      • @aniliitb10 对于同时更新的数据结构,“确切大小”没有多大意义。如果您可以排除并发更新,例如当多线程操作结束时,所有方法都将提供正确的大小。如果仍然可能发生并发更新,则大小可能会立即过时,size() 返回,无论它曾经多么精确。分段设计是使用Lock 实例来控制访问的想法的结果,以避免每个桶有一个Lock 的开销。新设计结合了无锁算法和synchronized
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-07
      • 2018-04-30
      • 2015-09-10
      • 1970-01-01
      • 2020-10-11
      相关资源
      最近更新 更多