逐行解读ConcurrentHashMap源码(一)

主要分析size()方法、put()方法、addCount()方法

一、部分属性解读

private static final int DEFAULT_CAPACITY = 16;//默认的初始化容量
private static final float LOAD_FACTOR = 0.75f;//默认的负载因子
static final int TREEIFY_THRESHOLD = 8;//链表结构转树结构的阈值
//两个比较重要的节点Hash值
static final int MOVED     = -1;//节点的Hash值为-1时(数组某索引下的首节点),表示当前槽位正在进行Rehash
static final int TREEBIN   = -2;//树结构根节点的Hash值

transient volatile Node<K,V>[] table;//Hash表中的数组结构,volatile保证原子性和可见性
private transient volatile Node<K,V>[] nextTable;//Rehash时使用的辅助数组,volatile修饰
private transient volatile long baseCount;//用于size()方法,记录在没有线程冲突的情况下,数组大小
private transient volatile int sizeCtl;//目前未知!!
private transient volatile int transferIndex;//目前未知!!
private transient volatile int cellsBusy;//在修改或者新建CounterCells对象状态时的变量锁,1为锁定状态,0为未锁定状态
private transient volatile CounterCell[] counterCells;//CounterCells对象数组,在size()方法中使用,当发生线程冲突的时候,使用该数组进行Map的辅助计数

二、方法解读

1.安全访问数组元素的方法

//获取当前索引上的Node节点
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
        return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
    }
//使用CAS操作修改数组上的节点
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                        Node<K,V> c, Node<K,V> v) {
        return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
    }
//给数组上的节点赋值
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
        U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
    }

2.size() - 并发环境下如何保证size的准确性

使用为size变量加锁,或者使用CAS操作修改size的值也能实现并发环境下对size的修改,但是效率低下。ConcurrentHashMap在线程不冲突和冲突两种情况下对size进行计数

//实际使用sumCount()方法计数
public int size() {
        long n = sumCount();
        return ((n < 0L) ? 0 :
                (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
                (int)n);
    }

final long sumCount() {
		//记得在第一节中提到的辅助数组吗?
		//HashMap总的个数=线程不冲突的情况下baseCount的计数+冲突的情况下CounterCell数组中的计数的和
		//在线程冲突的情况下,使用CounterCell数组辅助计数,将有冲突没有叠加在baseCount的数量保存在数组中.
        CounterCell[] as = counterCells; CounterCell a;
        long sum = baseCount;
        if (as != null) {
            for (int i = 0; i < as.length; ++i) {
                if ((a = as[i]) != null)
                    sum += a.value;
            }
        }
        return sum;
    }

3. put() - 如何并发的添加节点

//实际使用putVal方法
public V put(K key, V value) {
        return putVal(key, value, false);
    }
//关键方法!!
//参数中onlyIfAbsent默认设置为false表示,put一个key时,如果Map中存在这个key,覆盖其value的值
final V putVal(K key, V value, boolean onlyIfAbsent) {
		//使用了为什么ConcurrentHashMap不能存key-value为null的键值对
        if (key == null || value == null) throw new NullPointerException();
        //根据key的hash值计算一个扰动,避免在使用位运算计算数组下表时大量的冲突
        int hash = spread(key.hashCode());
        //某一个索引下节点的个数
        int binCount = 0;
        //无限循环-自旋处理
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            //如果put的时候,table还没初始化,先进行初始化,再次进入循环!
            if (tab == null || (n = tab.length) == 0)
                tab = initTable();
            //通过位运算计算数组下标,发现数组当前index的槽位为空,那么使用CAS操作修改该槽位的状态,如果修改成功,退出循环.多线程环境下,如果多个线程同时进行put,其他失败的线程则再次进入循环!
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            //数组中对应index槽位的第一个节点的Hash值为MOVED,参见一,说明这个槽位正在进行Rehash,在Rehash完成后,再次进入循环!
            else if ((fh = f.hash) == MOVED)
            	//参见后面关于该函数的解读!
                tab = helpTransfer(tab, f);
            else {
            //常规情况,即找到index索引下槽位的第一个节点
                V oldVal = null;
                //ConcurrentHasnMap 细粒度锁的体现,锁加载每个槽位的首节点上,不影响其他槽位节点
                //拿不到锁的线程,阻塞
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                    	//首节点的Hash大于0,参见一,说明此时是链表结构
                        if (fh >= 0) {
                            binCount = 1;
                            //小循环,直到找到一个存在的key或者将新的key添加到Map中为止
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                //找到一个存在的key,修改key的value,并退出小循环!
                                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;
                                //将新的值添加到链表末尾,退出小循环
                                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)
                        treeifyBin(tab, i);
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
        //记录size的关键代码!!
        addCount(1L, binCount);
        return null;
    }

addCount() - 如何使用baseCount和counterCells计算size

//check的作用:未知
private final void addCount(long x, int check) {
        CounterCell[] as; long b, s;
        if ((as = counterCells) != null ||
            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
            //之前已经发生过线程冲突(才会创建CounterCell数组)或者之前没有冲突,但是CAS修改baseCount的值失败(现在发生了冲突),使用CounterCell数组辅助计数.
            CounterCell a; long v; int m;
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
                !(uncontended =
                  U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
                 //如果之前没有发生过冲突,即CounterCell数组为空
                 //有过冲突,CounterCell数组不为空,但是在计算出的索引上的槽位是空,(该槽位还没有记录)
                 //槽位有记录的话,就CAS修改槽位的记录值,如果失败了,说明多个线程存在竞争
                 //fullAddCount参见后一个函数
                fullAddCount(x, uncontended);
                return;
            }
            if (check <= 1)
                return;
            s = sumCount();
        }
        //没懂!!
        if (check >= 0) {
            Node<K,V>[] tab, nt; int n, sc;
            while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
                   (n = tab.length) < MAXIMUM_CAPACITY) {
                int rs = resizeStamp(n);
                if (sc < 0) {
                    if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                        sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
                        transferIndex <= 0)
                        break;
                    if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
                        transfer(tab, nt);
                }
                else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                             (rs << RESIZE_STAMP_SHIFT) + 2))
                    transfer(tab, null);
                s = sumCount();
            }
        }
    }
 
 //wasUncontended表示是否存在竞争
 //此方法被使用到的情况分为两种:
 //第1种是没有竞争的情况,例如,CounterCell数组为空
 //第2中是没有竞争的情况,例如,某个槽位的CounterCell对象为空
 //第3种是存在竞争的情况,例如,CAS修改CounterCell数组上某个槽位的值失败
 private final void fullAddCount(long x, boolean wasUncontended) {
        int h;
        //类似计算Hash值
        if ((h = ThreadLocalRandom.getProbe()) == 0) {
            ThreadLocalRandom.localInit();      // force initialization
            h = ThreadLocalRandom.getProbe();
            wasUncontended = true;
        }
        boolean collide = false;                // True if last slot nonempty
        //无限循环-自旋
        for (;;) {
            CounterCell[] as; CounterCell a; int n; long v;
            //CounterCell数组已经存在
            if ((as = counterCells) != null && (n = as.length) > 0) {
            	//该槽位为空,对应了情况2->为该槽位创建一个CounterCell对象
                if ((a = as[(n - 1) & h]) == null) {
                	//cellsBusy 是一个锁,只有cellsBusy 为0的时候,才能进行下一步操作
                    if (cellsBusy == 0) {            // Try to attach new Cell
                        CounterCell r = new CounterCell(x); // Optimistic create
                        //CAS修改一个锁的状态
                        if (cellsBusy == 0 &&
                            U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                            boolean created = false;
                            //为该槽位生成一个CounterCell对象
                            try {               // Recheck under lock
                                CounterCell[] rs; int m, j;
                                if ((rs = counterCells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {
                                    rs[j] = r;
                                    created = true;
                                }
                            } finally {
                                cellsBusy = 0;
                            }
                            //成功就退出大循环,表面这一次的addCount()操作完成
                            //失败就自旋,再次进入循环
                            if (created)
                                break;
                            continue;           // Slot is now non-empty
                        }
                    }
                    //collide 值槽位的冲突
                    collide = false;
                }
                //CAS修改槽位冲突,自旋,重新进入循环
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                // CAS成功修改了该槽位上CounterCell对象的值
                else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                    break;
                 //槽位发生冲突,与HashMap拉链法不同,这里直接进行扩容
                else if (counterCells != as || n >= NCPU)
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
                else if (cellsBusy == 0 &&
                         U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                    try {
                    	//容量扩大为原来的两倍
                        if (counterCells == as) {// Expand table unless stale
                            CounterCell[] rs = new CounterCell[n << 1];
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            counterCells = rs;
                        }
                    } finally {
                        cellsBusy = 0;
                    }
                    collide = false;
                    //扩容后继续尝试
                    continue;                   // Retry with expanded table
                }
                h = ThreadLocalRandom.advanceProbe(h);
            }
            //CounterCell数组还没有初始化
            else if (cellsBusy == 0 && counterCells == as &&
                     U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                boolean init = false;
                try {                           // Initialize table
                    if (counterCells == as) {
                    //初始化一个大小为2的CounterCell数组
                        CounterCell[] rs = new CounterCell[2];
                        rs[h & 1] = new CounterCell(x);
                        counterCells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            }
            //尝试CAS修改baseCount的值,如果成功就使用BaseCount计数
            //个人感觉这一步都走不到
            else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
                break;                          // Fall back on using base
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值