ConcurrentHashMap源码分析

本文详细解析了ConcurrentHashMap的初始化table过程及put方法,涉及cas操作、table定位、链表与红黑树的处理。

初始化table:

/**
     * 初始化table
     * @return
     */
    private final ConcurrentHashMap.Node<K,V>[] initTable() {
        ConcurrentHashMap.Node<K,V>[] tab; int sc;
        /**
         * 死循环判断 table是否为null,或者长度为0
         */
        while ((tab = table) == null || tab.length == 0) {
            /**
             * 刚初始化sizeCtl=0,如果小于0说明有线程已经在初始化table
             */
            if ((sc = sizeCtl) < 0)
            /**
             * 当前线程让步
             */
                Thread.yield(); // lost initialization race; just spin
            /**
             * 使用cas操作,将sizeCtl的值设置为-1,设置成功返回ture,否则返回false
             */
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    /**
                     * 再次校验table
                     */
                    if ((tab = table) == null || tab.length == 0) {
                        /**
                         * DEFAULT_CAPACITY 默认长度是16
                         */
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        /**
                         * 常见长度为n的Node节点
                         */
                        @SuppressWarnings("unchecked")
                        ConcurrentHashMap.Node<K,V>[] nt = (ConcurrentHashMap.Node<K,V>[])new ConcurrentHashMap.Node<?,?>[n];
                        table = tab = nt;
                        /**
                         * 默认为12
                         */
                        sc = n - (n >>> 2);
                    }
                } finally {
                    /**
                     * 赋值sizeCtl
                     */
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

put方法:

// boolean onlyIfAbsent 参数默认是false,表示key相同时,是否更新旧值 false表示更新旧值,true表示不更新旧值
final V putVal(K key, V value, boolean onlyIfAbsent) {
    // key或者value是null,抛出空指针异常
    if (key == null || value == null) throw new NullPointerException();
    // 获取key的hash值,spread内部 (h ^ (h >>> 16)) & HASH_BITS
    int hash = spread(key.hashCode());
    // 这个是记录当前链表元素个数
    int binCount = 0;
    // 死循环
    for (Node<K,V>[] tab = table;;) {
         Node<K,V> f; int n, i, fh;
         // 第一次循环,此时的table、tab为null
         if (tab == null || (n = tab.length) == 0)
             // 初始化table对象,默认长度是16
             tab = initTable();
          // 循环,跟据(n - 1) & hash定位放入元素位置,如果当前索引没有元素,这个位置返回的是null,有元素返回的第一个元素
          else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
              // 通过cas操作,将这个元素放入i的位置,如果成功,结束循环
              if (casTabAt(tab, i, null,
                       new Node<K,V>(hash, key, value, null)))
                   break;                   // no lock when adding to empty bin
         }
         // 
          else if ((fh = f.hash) == MOVED)
              tab = helpTransfer(tab, f);
          // 在某个索引上面添加元素,链表或者红黑树
          else {
              V oldVal = null;
              // 最小力度加锁,只加锁某个索引
              synchronized (f) {
                  if (tabAt(tab, i) == f) {
                     // 链表
                     if (fh >= 0) {
                         binCount = 1;
                         for (Node<K,V> e = f;; ++binCount) {
                             K ek;
                             // 判断当前要put的值key是否已经存在
                            if (e.hash == hash &&
                                ((ek = e.key) == key ||
                               (ek != null && key.equals(ek)))) {
                                 // key已经存在
                                 oldVal = e.val;
                                 // 判断是否更新旧值
                                 if (!onlyIfAbsent)
                                      e.val = value;
                                break;
                            }
                            Node<K,V> pred = e;
                            // 判断当前元素是否是尾元素,不是,获取下一个继续循环
                            if ((e = e.next) == null) {
                                // 是,直接在当前元素后面追加添加的元素,
                                pred.next = new Node<K,V>(hash, key,
                                                 value, null);
                                break;
                            }
                        }
                    }
                    // 红黑树
                    else if (f instanceof TreeBin) {
                        Node<K,V> p;
                        binCount = 2;
                        if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                  value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            if (binCount != 0) {
                // 判断是否将链表转为红黑树
                if (binCount >= TREEIFY_THRESHOLD)
                     // 在转为红黑树前会怕断,当前元素个数是否超过64,超过64才会转为红黑树
                     reeifyBin(tab, i);
                // 是否是更新的旧值,是直接返回旧值,结束
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    // 
    addCount(1L, binCount);
    return null;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值