Concurrency JDK8代码解释中的ConcurrentHashmap

Concurrency JDK8代码解释中的ConcurrentHashmap,concurrency,java-8,concurrenthashmap,Concurrency,Java 8,Concurrenthashmap,我一直在试图理解JDK8中的ConcurrentHashMap函数,与JDK7中的函数形成对比(除了源代码之外,一些优秀的人(比如Richard)也可以很好地解释这些函数)。在JDK8中似乎已经做了很多更改-例如,不再有“段”本身,但不知怎的,我觉得这些更改是为了使代码更简单 我很难理解ConcurrentHashMap.putVal(…)方法,尤其是下面的一节-插入else{}时,是否直接锁定在“segment”列表的头部 else if ((fh = f.hash) == MOVED

我一直在试图理解JDK8中的ConcurrentHashMap函数,与JDK7中的函数形成对比(除了源代码之外,一些优秀的人(比如Richard)也可以很好地解释这些函数)。在JDK8中似乎已经做了很多更改-例如,不再有“段”本身,但不知怎的,我觉得这些更改是为了使代码更简单

  • 我很难理解ConcurrentHashMap.putVal(…)方法,尤其是下面的一节-插入else{}时,是否直接锁定在“segment”列表的头部

        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {//...}
    
  • 对ConcurrentHashMap.casTabAt(…)的代码也不是很确定

  • 另外,关于JDK8中ConcurrentHashMap.get(Object key)的源代码,它是否完全没有锁(我没有看到任何锁,如果是的话,它如何在没有锁的情况下工作,因为我也没有看到“重试”循环)或者存在某种我没有观察到的乐观锁


  • 如果有人能提供一些提示,我将不胜感激。

    关于
    putVal(K键,V值,布尔值)
    方法

    每个bin/bucket都包含一个
    散列
    字段,它以一种非常巧妙的方式结合了两个目的:

    • 对于常规容器(大多数容器仅包含单个项),它存储mapped here键的哈希代码。但顶部位已清除(它始终设置为0)
    • 对于特殊箱子(目前有3种),它包含一个特殊的负值。聪明的部分是,您只需要最上面的一位来区分正值和负值,从而区分常规存储箱和特殊存储箱。区分不同类型的特殊箱子可以自由使用剩余的31位
    本节

    else if ((fh = f.hash) == MOVED)
        tab = helpTransfer(tab, f);
    else {//...}
    
    是在发现映射不为空且您尝试映射的密钥的存储箱不为空后的第一次检查

    如果你发现的箱子是一种特殊类型的箱子,那就很满意了——一种转发箱。需要转发箱,因为调整大小是并行和迭代进行的,并且已经传输(到新表)的条目仍然需要访问(通过旧表中的转发箱)

    关于
    casTabAt((节点[]选项卡,int i,节点c,节点v)
    方法

    casTabAt()
    方法用于使用对象引用的比较和交换操作来自动设置映射项。在
    casTabAt()的几乎所有位置,您仍然可以看到典型的CAS循环
    已被使用-您构造要放置的对象,然后尝试将其放置在正确的位置。如果在CAS尝试之前进行复杂的构造感到奇怪,您可以查看Jeff Preshing的

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

    关于
    get(对象键)
    方法

    get()
    方法可以在没有任何锁定的情况下离开,因为在大多数情况下,使用
    volatile
    语义(通过前面提到的
    casTabAt()
    方法和相关的
    tabAt()
    方法)设置和检索箱子内容。如果bin包含映射到同一bin的条目的红黑树,则情况更为棘手,并且您可以看到访问的
    TreeBin
    中的遍历始终在
    同步的
    块中完成