Java ConcurrentHashMap初始化

Java ConcurrentHashMap初始化,java,concurrency,concurrenthashmap,Java,Concurrency,Concurrenthashmap,我目前正在阅读jdk1.8中ConcurrentHashMap的源代码,我发现initalTable()方法有点让人困惑 公共类ConcurrentHashMap扩展了AbstractMap 实现ConcurrentMap,可序列化{ //某些字段在该方法中旋转 瞬态易失性节点[]表; 私有瞬态volatile int sizeCtl; 私有最终节点[]initTable(){ 节点[]选项卡;int sc; while((tab=table)==null | | tab.length==0){

我目前正在阅读jdk1.8中ConcurrentHashMap的源代码,我发现
initalTable()
方法有点让人困惑

公共类ConcurrentHashMap扩展了AbstractMap
实现ConcurrentMap,可序列化{
//某些字段在该方法中旋转
瞬态易失性节点[]表;
私有瞬态volatile int sizeCtl;
私有最终节点[]initTable(){
节点[]选项卡;int sc;
while((tab=table)==null | | tab.length==0){
如果((sc=sizeCtl)<0)
Thread.yield();//丢失了初始化竞争;只需旋转
否则如果(U.compareAndSwapInt(此,SIZECTL,sc,-1)){
试一试{
如果((tab=table)==null | | tab.length==0){
int n=(sc>0)?sc:默认容量;
@抑制警告(“未选中”)
节点[]nt=(节点[])新节点[n];//位置1
table=tab=nt;//位置2
sc=n-(n>>>2);
}
}最后{
sizeCtl=sc;
}
打破
}
}
返回选项卡;
}
}
所以主要的困惑主要集中在以下几点:

  • 为什么我们需要
    while((tab=table)==null | | tab.length==0)
    来检查
    table
    是否为null,其长度是否为0,并确定是否继续循环,因此它仅在这两个条件都为false时才退出。我无法想象
    不是null而是长度为0的情况,因为它是在
    else if
    块中初始化的,其中n的值总是大于0。那为什么我们需要两者而不是一个呢
  • 二,。第二个问题不是问题,我只是想看看我的假设是否有道理。因此,设想两个线程随后以非常接近的方式运行。后一个线程可以进入
    else if
    块如果前一个线程没有达到
    table
    或place 2的初始化点,也没有更新
    sizeCtl
    ,那么第二个线程将能够进入
    else if
    块,那么
    table
    字段将再次初始化?这种情况会在罕见的情况下发生吗?我知道这不会影响程序的正确性,但在某些情况下会发生吗

    希望任何人都能给我一个澄清。提前谢谢


    更新:
    我忘记了一个事实,一个成功的CAS操作将把
    sizeCtl
    交换为-1,这将阻止其他线程进入
    else if
    块,但是我仍然不清楚为什么我们在
    if
    中需要两个条件,而
    单独分析方法
    initTable
    是不够的。您需要考虑其他也可以修改
    sizeCtl
    字段的方法。在那里,您会注意到,例如,
    transfer
    使用CAS将
    sizeCtl
    减少一个。我不确定这是否会在任何时间点导致
    table.length==0
    ,但请注意,整个过程要复杂得多,并且
    initTable
    可以与resize操作同时调用。

    compareAndSwap是原子的;该检查确保只有一个线程可以进入该块。@LouisWasserman感谢您的提示,是的,我忘记了交换,如果一个线程进入块
    sizeCtl
    已交换为-1,那么阻止其他线程进入该块,对吗?但是第一点呢?你能帮我照一下吗?