我一直在尝试理解JDK8中的ConcurrentHashMap函数,与JDK7中的它是如何形成对比的(除了源代码之外,还可以找到一些很好的人解释,例如Richard {{ 3}})。它看起来在JDK8中已经发生了很大变化 - 例如没有更多的细分'本身,但不知怎的,我觉得这些变化是为了让代码更简单?
我很难理解方法ConcurrentHashMap.putVal(...),尤其是以下部分 - 这是直接锁定在'段&#39的头部;无论如何列表插入else {}?:
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {//...}
不太确定ConcurrentHashMap.casTabAt(...)的代码。
另外,关于JDK8中ConcurrentHashMap.get(Object key)的源代码,它是否完全没有锁定(我没有看到任何内容,如果是这样,它如何在没有锁定的情况下工作我没有看到一个循环“再试一次”?或者还有一些我没有观察到的乐观锁定?
感谢是否有人可以提供一些提示。
答案 0 :(得分:6)
关于putVal(K key, V value, boolean onlyIfAbsent)
方法
每个bin / bucket包含一个hash
字段,它以非常聪明的方式结合了两个目的:
本节
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {//...}
是在发现地图不为空并且您尝试映射的密钥的bin不为空之后的第一次检查。
如果您找到的垃圾箱是特殊类型的垃圾箱 - 转发箱,则会感到满意。转发箱是必需的,因为调整大小是同时并且迭代完成并且已经转移(到新表),条目仍然需要可访问(通过旧表中的转发箱)。
关于casTabAt((Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v)
方法
casTabAt()
方法用于使用对象引用的比较和交换操作以原子方式设置映射条目。您仍然可以在几乎所有使用casTabAt()
的地方看到典型的CAS循环 - 您构建了要放置的对象,然后尝试将CAS放在正确的位置。如果复杂的结构可以在CAS尝试之前感到奇怪,你可以看一下Jeff Preshing的You Can Do Any Kind of Atomic Read-Modify-Write Operation。
从某种意义上说,ConcurrentHashMap
仍然使用条带锁定,但具有更精细的锁粒度(竞争区域现在从多仓区段最小化到单个区间)并且锁几乎完全被CAS操作取代。 / p>
关于get(Object key)
方法
get()
方法可以在没有任何锁定的情况下离开,因为在大多数情况下,使用volatile
语义(通过前面提到的casTabAt()
方法和相关的{{}}设置和检索bin内容{1}}方法)。如果bin包含映射到同一bin的红黑树条目,则情况比较棘手,您可以看到访问tabAt()
内的遍历始终在TreeBin
块中完成。