美文网首页
ConcurrentHashMap源码导读之initTable(

ConcurrentHashMap源码导读之initTable(

作者: 犄角芝士 | 来源:发表于2018-11-11 22:48 被阅读0次

    一、简介

    ConcurrentHashMap在初始化table时调用的方法。方法用private final修饰。

    二、源码

    先介绍方法内的三个全局变量

    • sizeCtl
    /**
     * 用于控制table初始化和resize的参数
     * -1表示初始化
     * 其他负数+1表示正在进行resize的线程数,为了与-1区别开
     * 0代表默认状态
     * 在初始化之后,该值表示下次需要resize时map内元素的个数
     */
    private transient volatile int sizeCtl;
    
    • DEFAULT_CAPACITY
    /**
     * 默认初始化table的容量,必须是2的n次方
     */
    private static final int DEFAULT_CAPACITY = 16;
    

    初始化ConcurrentHashMap是传入的容量不是2的n次方时会发生什么?

    • SIZECTL
    // 初始化时与sizeCtl值相等,为0
    private static final long SIZECTL;
    
    static {
            try {
                U = sun.misc.Unsafe.getUnsafe();
                Class<?> k = ConcurrentHashMap.class;
                SIZECTL = U.objectFieldOffset
                    (k.getDeclaredField("sizeCtl"));
            } catch (Exception e) {
                throw new Error(e);
            }
        }
    

    为什么SIZECTL需要通过反射来设置?

    下面先贴出initTable()源码

    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;
    }
    

    可以看出table的初始化在一个cas方法中进行,当table为null或者长度为0时进入while方法。

    进入之后判断sizeCtl的指,如果小于0则线程让步。由于初始状态sizeCtl是等于0的,说明前面已经有线程进入了elseif这部分,将sc的值置为-1,表示正在初始化。

    如果sc大于0,则取sc,否则取默认容量16。然后计算下一次元素数量达到多少时需要resize。

    为什么else..if代码块中还要判断一次table是否已经初始化

    相关文章

      网友评论

          本文标题:ConcurrentHashMap源码导读之initTable(

          本文链接:https://www.haomeiwen.com/subject/eewffqtx.html