Concurrency ConcurrentHashMap,JDK8中改进了哪些并发特性

Concurrency ConcurrentHashMap,JDK8中改进了哪些并发特性,concurrency,java-8,concurrenthashmap,Concurrency,Java 8,Concurrenthashmap,任何并发专家都可以在ConcurrentHashMap中解释哪些并发特性与以前的JDK相比有所改进吗。在Java8之前,每个ConcurrentHashMap都有一个在构建时固定的“并发级别”。出于兼容性方面的原因,尽管没有以原来的方式使用它,但仍然存在一些问题。映射被拆分为尽可能多的段(取决于其并发级别),每个段都有自己的锁,因此从理论上讲,如果它们都以不同的段为目标(这取决于散列),则可能会有多达并发级别的并发更新 在Java8中,每个hash bucket都可以单独更新,所以只要没有has

任何并发专家都可以在ConcurrentHashMap中解释哪些并发特性与以前的JDK相比有所改进吗。在Java8之前,每个
ConcurrentHashMap
都有一个在构建时固定的“并发级别”。出于兼容性方面的原因,尽管没有以原来的方式使用它,但仍然存在一些问题。映射被拆分为尽可能多的段(取决于其并发级别),每个段都有自己的锁,因此从理论上讲,如果它们都以不同的段为目标(这取决于散列),则可能会有多达并发级别的并发更新

在Java8中,每个hash bucket都可以单独更新,所以只要没有hash冲突,就可以有与其当前容量一样多的并发更新。这与保证原子更新的方法等新特性是一致的,因此,至少锁定更新的哈希桶。在最好的情况下,它们实际上只锁定一个桶


此外,
ConcurrentHashMap
得益于应用于所有类型的哈希映射的一般哈希改进。当某个bucket存在散列冲突时,实现将求助于该bucket中的排序映射式结构,从而降低为
O(log(n))
复杂性,而不是
O(n)
搜索bucket时旧实现的复杂性。

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

  • 延迟初始化:在JDK8中,只有在向映射添加某些实体时,才会分配用于每个段的内存。在JDK7中,这是在创建映射时完成的
  • JDK8中增加了一些新功能,如forEach、reduce、search等
  • 内部结构变化:jdk8中使用了树形树(红黑树)以提高搜索效率
Hi 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
实例控制访问的思想的结果,以避免为每个bucket设置
Lock
的开销。新设计结合了无锁算法和同步算法。