JDOK8中的ConcurrentHashmap代码说明

Sto*_*ika 6 concurrency concurrenthashmap java-8

我一直在尝试理解JDK8中的ConcurrentHashMap函数,与JDK7中的它是如何形成对比的(除了源代码之外,还可以找到很好的解释,例如Richard http:// www .burnison.ca/articles/the-concurrent-of-concurrenthashmap).它看起来在JDK8中已经发生了相当大的变化 - 例如,本身没有更多的"段",但不知怎的,我觉得这些变化是为了让代码变得更简单?

  1. 我很难理解方法ConcurrentHashMap.putVal(...),特别是以下部分 - 这个直接锁定在'segment'列表的头部,无论如何要插入else {}?:

        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {//...}
    
    Run Code Online (Sandbox Code Playgroud)
  2. 对ConcurrentHashMap.casTabAt(...)的代码也不太确定.

  3. 另外,关于JDK8中ConcurrentHashMap.get(Object key)的源代码,它是否完全没有锁定(我没有看到任何内容,如果是这样的话,没有锁定它是如何工作的,因为我看不到循环'再试一次?或者还有一些我没有观察到的乐观锁定?

感谢是否有人可以提供一些提示.

Dim*_*rov 6

关于putVal(K key, V value, boolean onlyIfAbsent)方法

每个bin/bucket包含一个hash字段,它以非常聪明的方式结合了两个目的:

  • 对于常规箱(大多数箱仅包含单个项),它存储映射到此键的哈希码.顶部位被清除(它总是设置为0).
  • 对于特殊箱(目前有3种类型),它包含一个特殊的负值.聪明的部分是你只需要顶部位来区分正值和负值,因此可以从特殊箱中区分常规箱.区分不同类型的特殊箱可以自由使用剩余的31位.

这个部分

else if ((fh = f.hash) == MOVED)
    tab = helpTransfer(tab, f);
else {//...}
Run Code Online (Sandbox Code Playgroud)

是在发现地图不为空并且您尝试映射的键的bin不为空之后的第一次检查.

如果您找到的垃圾箱是特殊类型的垃圾箱之一 - 转发箱,则会感到满意.转发箱是必需的,因为调整大小是同时并且迭代地完成并且已经转移(到新表),条目仍然需要可访问(通过旧表中的转发箱).

关于casTabAt((Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v)方法

casTabAt()方法用于使用对象引用的比较和交换操作以原子方式设置映射条目.您仍然可以在几乎所有使用的地方看到典型的CAS循环casTabAt()- 您构建要放置的对象,然后尝试将CAS放在合适的位置.如果在CAS尝试之前复杂的结构感觉很奇怪,你可以看一下Jeff Preshing的你可以做任何一种原子读 - 修改 - 写操作.

从某种意义上说,ConcurrentHashMap仍然使用条带锁定,但具有更精细的锁定粒度(竞争区域现在从多仓区段最小化到单个区间)并且锁几乎完全被CAS操作取代.

关于get(Object key)方法

get()方法可以在没有任何锁定的情况下离开,因为在大多数情况下,使用volatile语义(通过上述casTabAt()方法和相关tabAt()方法)来设置和检索bin内容.如果bin包含映射到同一bin的红黑树条目,则情况比较棘手,您可以看到访问内的遍历TreeBin总是在synchronized块中完成.