ConsurrentHashMap InitTable 疑问与思考

ConsurrentHashMap InitTable 疑问与思考

ConsurrentHashMap 作为单机下经常使用的一个线程安全Map值得我们学习一下,以下其初始化的过程的代码,以及学习过程中遇到的疑惑与自己的思考

 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];
                        table = tab = nt;
                        sc = n - (n >>> 2);
                    }
                } finally {
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }
1、多线程下该方法不加锁时如何保证线程安全的

通常意义下的加锁,都是去检查一个东西是否被“其他”持有,如果被持有则本次加锁尝试失败。这里也是同样的思路,通过判断sc是否小于零来判断当前是否有其他线程操作,如果有其他线程操作到下面sc会被修改成**-1**来表示被加锁,同时CAS的操作保证了只能由一个线程修改成功,即只有一个线程能获得锁

2、为什么使用while 而不是if

initTable 的方法是为了完成初始化返回一个node数组,所以没有**“获取锁”线程需要陷入等待,等待其他线程完成初始化,所以该方法使用了while来让没有“获取锁”**的线程不断循环等待。

3、为什么使用Thread.yield(),而不是return、sleep

第二个问题说到需要循环等待所有自然不可能提前return,那为什么不用sleep,因为我们并不知道另一个线程什么时候完成初始化,也就不知道sleep多少秒,多了、少了都不合适,所以干脆就让出cpu时间片,等下一次再看看,如果其他线程完成了初始化就返回,否则重复以上。这其实就是源码中注释表达的含义:lost initialization race; just spin,退出竞争,不断自旋。

你可能感兴趣的:(Java基础,java,jvm,开发语言)