ConcurrentHashMap源码

Posted da-peng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ConcurrentHashMap源码相关的知识,希望对你有一定的参考价值。

putval源码

final V putVal(K key, V value, boolean onlyIfAbsent) {
        //判断参数是否合格
        if (key == null || value == null) throw new NullPointerException();
        int hash = spread(key.hashCode());
        int binCount = 0;
        
        //开始死循环
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            //如果table还没有初始化
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            //传入参数tab类与偏移地址(n-1)&hash,通过CAS方法获取对应目标的值,f,如果对应目标为空,就直接插入
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                // CAS 进行插入,参数(类,偏移地址,预测值,当前值)
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin 没有锁,如果是空bin的话
            }
            
            //如果在进行扩容,则先进行扩容操作??????????????????????????????
            else if ((fh = f.hash) == MOVED)
                tab = helpTransfer(tab, f);
            //hash冲突
            else {
                V oldVal = null;
                //在桶子上加锁,每一个桶子都可以是一个锁
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        //fh>=0说明,说明是一个链表
                        if (fh >= 0) {
                            //统计链表的长度,看是否需要转变为一个红黑树
                            binCount = 1;
                            //循环查找
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                //找到相同的key
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                Node<K,V> pred = e;
                                //没找到相同的key,在最后加上新节点
                                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)
                        //根据table,和偏移量转换一个桶子后面的链表为红黑树
                        treeifyBin(tab, i);
                    //返回旧的value值
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        //统计size,并且检查是否需要扩容
        addCount(1L, binCount);
        return null;
    }

inittable()

    private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        //检查是否为空
        while ((tab = table) == null || tab.length == 0) {
            if ((sc = sizeCtl) < 0)     //sizeCtl<0表示其他线程已经在初始化了或者扩容了,挂起当前线程 
                Thread.yield(); // lost initialization race; just spin
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {      //将SIZECTL置为-1,表示有一个线程正在初始化table
                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;
    }

 

以上是关于ConcurrentHashMap源码的主要内容,如果未能解决你的问题,请参考以下文章

ConcurrentHashMap 源码分析

JDK12 concurrenthashmap源码阅读

两万五千字的ConcurrentHashMap底层原理和源码分析

从源码看concurrentHashMap的线程安全

JDK源码系列之ConcurrentHashMap

ConcurrentHashmap核心源码分析