提问者:小点点

Java初始化


我目前正在阅读jdk1.8中ConCurrentHashMap的源代码,我发现initalTable()方法有点混乱。

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {
//some fields revolved in this method
transient volatile Node<K,V>[] table;
private transient volatile int sizeCtl;

  private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        while ((tab = table) == null || tab.length == 0) {
            if ((sc = sizeCtl) < 0)
                Thread.yield(); // lost initialization race; just spin
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    if ((tab = table) == null || tab.length == 0) {
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];//place 1
                        table = tab = nt;//place 2
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }
}

所以主要的困惑主要集中在以下几点:

  1. 为什么我们需要while((tab=table)==null||tab. long==0)来检查table不为null且其长度不为0并确定是否继续循环,因此它仅在两个条件均为false时退出。我只是想不出table不为null但长度为0的情况,因为它是在else块中初始化的,其中n始终被赋予大于0的值。那为什么我们需要两者而不是只有一个?

2.第二个问题不是问题,而是我只想检查我的假设是否有意义。所以想象两个线程随后以非常接近的方式运行。如果较早的线程没有到达table的初始化点或place 2也没有更新sizeCtl,那么第二个线程将能够进入else if块,因此table字段将再次初始化?这种情况会在极少数情况下发生吗?我知道这不会影响程序的正确性,但在某些情况下会发生吗?

希望任何人能给我一个澄清。事先感谢。

更新:我忘记了一个成功的CAS操作会将sizeCtl交换为-1,这将阻止其他线程进入else if块,但我仍然不清楚为什么我们需要ifwhile中的两个条件。


共1个答案

匿名用户

单独分析initTable方法是不够的。您需要考虑其他方法,这些方法也可以修改tablesizeCtl字段。在那里您会注意到,例如传输使用CAS将sizeCtl减少了一个。我不确定这是否会导致任何时间点的table. long==0,但请注意,整个过程要复杂得多,initTable可以与调整大小操作同时调用。