手撕源码——ConcurrentHahsMap(1.8)常用方法

继承和实现的类、接口

​ 继承AbstractMap<K,V>抽象父类

​ 实现ConcurrentMap<K,V>(map规范)、Serializble(序列化)

成员常量

	// 表最大容量
	private static final int MAXIMUM_CAPACITY = 1 << 30;
	// 表默认容量
	private static final int DEFAULT_CAPACITY = 16;
	// 数组最大容量
	static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	// 默认并发数量
	private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
	// 负载因子
	private static final float LOAD_FACTOR = 0.75f;
	// 链表转换为红黑树的阈值
	static final int TREEIFY_THRESHOLD = 8;
	// 红黑树转链表的阈值
	static final int UNTREEIFY_THRESHOLD = 6;
	// 链表转红黑树的最小容量
	static final int MIN_TREEIFY_CAPACITY = 64;
	// 每次进行转移的最小值
	private static final int MIN_TRANSFER_STRIDE = 16;
	// 生成sizeCtl所使用的bit位数
	private static int RESIZE_STAMP_BITS = 16;
	// 帮助扩容时使用的元素,最大线程数
	private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
	// 帮助扩容时使用的元素,记录sizeCtl中的大小所需进行的偏位移数
	private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
	/*
     * Encodings for Node hash fields. See above for explanation.
     */
    static final int MOVED     = -1; // hash值是-1,表示这是一个forwardNode节点,表示有线程处理过了
    static final int TREEBIN   = -2; // hash值是-2  表示这时一个TreeBin节点,表示这是一个树节点
    static final int RESERVED  = -3; // hash for transient reservations
    static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash

成员变量

	// 存储元素的数组,总是2的幂次倍
	transient volatile Node<K,V>[] table;
	private transient volatile Node<K,V>[] nextTable;
	// 计数
	private transient volatile long baseCount;
	//hash表初始化或扩容时的一个控制位标识量。
	//负数代表正在进行初始化或扩容操作
	//-1代表正在初始化
	//-N 表示有N-1个线程正在进行扩容操作
	//正数或0代表hash表还没有被初始化,这个数值表示初始化或下一次进行扩容的大小
	private transient volatile int sizeCtl;
	// 扩容下  另一个表的索引
	private transient volatile int transferIndex;
	// 旋转锁
	private transient volatile int cellsBusy;
	//  countterCell表
	private transient volatile CounterCell[] counterCells;
	// 视图
	private transient KeySetView<K,V> keySet;
	private transient ValuesView<K,V> values;
	private transient EntrySetView<K,V> entrySet;

添加/修改put()

	// 参数不能为null
	public V put(K key, V value) {
        // 调用putVal()完成添加(修改)操作
        return putVal(key, value, false);
    }
final V putVal(K key, V value, boolean onlyIfAbsent) {
    	// 参数为空直接返回空指针异常
        if (key == null || value == null) throw new NullPointerException();
    	// key.hashCode获取hash值,用spread()计算下标位置
        int hash = spread(key.hashCode());
    	// 记录单个桶中的元素个数
        int binCount = 0;
    	// 循环使用了CAS机制,不成功则一直循环
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            // 判断数组是否为空或长度是否为0
            if (tab == null || (n = tab.length) == 0)
                // 对数组进行初始化
                tab = initTable();
            // tabAt(数组,下标)用来获取指定下标的内容,内部是Unsafe的getObjectVolatile方法,保证线程拿到的是table中的最新元素。
            // (n-1)&hash计算下标
            // 如果为空
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                // 通过CAS方式替换下标i的节点
                // casTabAt(数组,下标,目标值,要修改的值)
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            // 发生了hash冲突,数组正在进行扩容
            else if ((fh = f.hash) == MOVED)
                // 协助扩容
                tab = helpTransfer(tab, f);
            // 以上条件都不满足,即底层数组存在且计算出的数组下标有值且数组没有进行扩容,则去添加/替换元素
            else {
                V oldVal = null;
                // 结点上锁,这里的结点可以理解为hash值相同组成的链表的头结点
                // 也代表了ConcurrentHashMap是线程安全的	
                synchronized (f) {
                    // 判断是否为头节点
                    if (tabAt(tab, i) == f) {
                        // 判断是否为链表
                        if (fh >= 0) {
                            binCount = 1;
                            // 对链表进行遍历
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                // 当前节点的hash值和要插入的hash值,key相同
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    // 应该是记录当前节点旧值
                                    oldVal = e.val;
                                    // 传入的值为false,这里为true
                                    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;
                            // 这里调用的是内部的TreeBin的putTreeVal,不是hashMap的
                            // 返回值不为空,代表找到了相同的key,为该节点的value值
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                // 记录旧值
                                oldVal = p.val;
                                // 这里传入false,为true
                                if (!onlyIfAbsent)
                                    // 对旧值进行替换
                                    p.val = value;
                            }
                        }
                    }
                }
                // binCount记录了遍历了的节点数,如果节点数不为0
                if (binCount != 0) {
                    // 判断节点数量是否大于树化条件
                    if (binCount >= TREEIFY_THRESHOLD)
                        // 执行树化操作
                        // 树化,这里还会判断一下整体容量是否大于64.如果小于64进行扩容不树化,这里会再次加锁
                        treeifyBin(tab, i);
                    // 判断旧值是否为空
                    if (oldVal != null)
                        // 返回旧值
                        return oldVal;
                    break;
                }
            }
        }
    	// 维护数组长度(条件允许时会扩容)
        addCount(1L, binCount);
        return null;
    }

数组维护addCount()

private final void addCount(long x, int check) {
    	// CounterCell用来记录元素个数,总数之的分值分别记录在每个cell中
    	// CounterCell的每个元素,都存储一个元素个数,我们使用的size()就是通过循环CounterCell累加而得到的
        CounterCell[] as; long b, s;
    	// CounterCell数组是否为空
        if ((as = counterCells) != null ||
            // CounterCell为空,则使用cas对baseCount进行+操作
            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
            // 如果CounterCell数组不为空,或者CounterCell数组为空且使用CAS对baseCount进行加操作失败,进入这里
            CounterCell a; long v; int m;
            // 标记是否 没有 多线程竞争,这里的true为无竞争
            boolean uncontended = true;
            // 如果CounterCell数组为空
            if (as == null || (m = as.length - 1) < 0 ||
                // 随机计算一个CounterCell数组中某个位置的对象为空
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
                // 或者对该CounterCell的value值使用CAS相加时失败  uncontended=false 表示有竞争
                !(uncontended =
                  U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
                // 任何一个条件成功都会执行
                // fullAddCount()主要用来初始化CounterCell
                fullAddCount(x, uncontended);
                return;
            }
            // 使用CAS修改CELLVALUE成功,则会到这里
        	// check为binCount binCount始终大于等于0 
        	// binCount等于0 表示在put值时Node数组对应的元素为空
        	// binCount等于1 表示在put值时,是链表且链表只有一个key相同的元素,替换
        	// 直接return,不需要再去检查是否需要扩容
            if (check <= 1)
                return;
            // 计算此时的map大小
            s = sumCount();
        }
    	// 判断是否需要扩容
        if (check >= 0) {
            Node<K,V>[] tab, nt; int n, sc;
            // 如果元素个数>=扩容阈值或者-1
            while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
                   (n = tab.length) < MAXIMUM_CAPACITY) {
                // 获取一个扩容的标记
                int rs = resizeStamp(n);
                // <0则代表其他线程正在扩容,sc为sizeCtl
                if (sc < 0) {
                    //这5个条件只要有一个条件为true,说明当前线程不能帮助进行此次的扩容,直接跳出循环
                    //sc >>> RESIZE_STAMP_SHIFT!=rs 表示比较高RESIZE_STAMP_BITS位生成戳和rs是否相等,相同
                    //sc=rs+1 表示扩容结束
                    //sc==rs+MAX_RESIZERS 表示帮助线程线程已经达到最大值了
                    //nt=nextTable -> 表示扩容已经结束
                    //transferIndex<=0 表示所有的transfer任务都被领取完了,没有剩余的hash桶来给自己自己好这个线程来做transfer
                    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);
                }
                // 没有扩容,(rs << RESIZE_STAMP_SHIFT) +2 表示只有一个线程正在进行扩容
                else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                             (rs << RESIZE_STAMP_SHIFT) + 2))
                    // 扩容
                    transfer(tab, null);
                // 从新计数,从而判断是否需要下一轮扩容
                s = sumCount();
            }
        }
    }

扩容机制transfer()

private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
    	// stride和CPU相关
        int n = tab.length, stride;
    	// 先判断CPU核数是否大于1 如果不大于1则没有必要多线程扩容
    	// 计算需要迁移多少个hash桶,小于16的话直接赋值16
    	// 这里的目的是让每一个CPU处理的桶数量一样,避免出现转移任务分布不均匀。桶较少时,默认一个CPU处理16个桶
        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
            stride = MIN_TRANSFER_STRIDE; 
    	// 外围会保证第一个发起扩容请求的线程调用此方法时参数nextTable为null,即新表未初始化
    	// initiating只能有一个线程进行构造nextTable,如果别的线程进入发现不为空就不用构造nextTable了
        if (nextTab == null) {            
            try {
                @SuppressWarnings("unchecked")
                // 容量翻倍
                Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
                nextTab = nt;
            } catch (Throwable ex) {      
                // 扩容失败,sizeCtl使用Integer最大值
                sizeCtl = Integer.MAX_VALUE;
                return;
            }
            // 将新的数组赋值给成员变量
            nextTable = nextTab;
            // n为旧tab的length
            transferIndex = n;
        }
    	// 新数组的长度
        int nextn = nextTab.length;
    	// 创建一个新的fwd节点,用于占位,别的线程发现这个位置是ForwardingNode节点时,跳过
        ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
    	// 值为true时,表示该节点已经处理过了,说明需要进行下一个下标(i--)
    	// 值为false时,就不能进行下一个下标,需要将当前下标处理完毕才能继续推进
        boolean advance = true;
    	// 如果为true,则此方法结束
        boolean finishing = false; // to ensure sweep before committing nextTab
    	// 死循环,i是数组索引下标,bound为边界(当前线程可以处理的当前桶区间最小下标)
        for (int i = 0, bound = 0;;) {
            Node<K,V> f; int fh;
            // 这里有点绕
            // 当advance=true时才可以进行下一个位置的迁移操作(i--)
            // i指向transferIndex,dound指向transferIndex-stried
            while (advance) {
                int nextIndex, nextBound;
                // i先自减1,当i<dound或者最后一个线程转移的操作结束
                // advance赋值为false,不在计算i和bound
                if (--i >= bound || finishing)
                    // 防止在没有处理成功一个桶的情况下进行下一轮推送
                    advance = false;
                // 这里有两层意思
                // 1、当一个线程进入时,获取最新的转移下标
                // 2、线程处理完自己的区间后,如果还有剩余区间,则再次获取
                else if ((nextIndex = transferIndex) <= 0) {
                    // transferIndex <= 0 时,代表没有区间了,扩容结束,当前线程退出
                    i = -1;
                    advance = false;
                }
                // cas机制修改transferIndex
                // 判断REANSFERINDEX和nextIndex是否相等
                // nextbound为此次线程迁移任务的边界,从后往前执行
                // 下一个索引位置大于区块末尾,则nextIndex减去前一个区块,否则为0
                else if (U.compareAndSwapInt
                         (this, TRANSFERINDEX, nextIndex,
                          nextBound = (nextIndex > stride ?
                                       nextIndex - stride : 0))) {
                    // 这个值就是当前线程可以处理的最小当前区间最小下标
                    bound = nextBound;
                    // 初次对i 赋值,这个就是当前线程可以处理的当前区间的最大下标
                    i = nextIndex - 1;
                    // 防止当前线程没有成功处理一个桶的情况下进行下一轮推送
                    advance = false;
                }
            }
            // i<0(不在table下标内,按上面判断的意思,是领取最后一段区间的线程扩容结束)
            // i>= (n=table.length),这里的i应该为当前线程处理hash桶结束的位置
            // i+n >= nextn,nextn在上面容量进行了翻倍操作,但是长度和table长度一样
            if (i < 0 || i >= n || i + n >= nextn) {
                int sc;
                // 完成了扩容
                if (finishing) {
                    // 赋予空值
                    nextTable = null;
                    // 将新表赋值给全局table
                    table = nextTab;
                    // 更新扩容阈值,为现在容量的0.75倍
                    sizeCtl = (n << 1) - (n >>> 1);
                    return;
                }
                // 在addcount()中,用了(rs << RESIZE_STAMP_SHIFT) + 2,然后每多一个线程参与进来sc+1
                // 所以在这里要对sc-1,表示该线程任务已经结束
                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                    // 如果sc-2不等于标识符左移16位,说明该线程不是最后一个扩容的线程
                    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                        // 结束此线程
                        return;
                    // 将finishing变量更新为true
                    finishing = advance = true;
                    // 再次循环检查表
                    i = n; // recheck before commit
                }
            }
            // 判断老tab中i下标位置是否为空
            else if ((f = tabAt(tab, i)) == null)
                // 为空,使用cas方式将该位置放入一个fwd节点,再次推进一个下标
                advance = casTabAt(tab, i, null, fwd);
            // 如果该节点的hash值为moved
            else if ((fh = f.hash) == MOVED)
                // 说明别的线程已经处理过了,再次推进一个下标
                advance = true;
            // 走到这里,说明这个位置上有值存在且不是占位符,
            else {
                // 对该节点上锁,防止putVal向链表插入数据
                synchronized (f) {
                    // 这里的判断可能会漏掉某一个桶,进行下一个桶的数据迁移。
                    // 判断i下标处的桶节点是否和f相同
                    if (tabAt(tab, i) == f) {
                        // 低位桶,高位桶
                        Node<K,V> ln, hn;
                        // fh为f的hash值,大于0时,TreeBin的hash是-2,代表是链表节点
                        if (fh >= 0) {
                            // 这里的链表拆分逻辑,我把莫纳-鲁道博主的介绍放在这里,大家看一下方便理解
                            /* 1、对老长度进行与运算(第一个操作数的的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0);
                            2、由于 Map 的长度都是 2 的次方(000001000 这类的数字),那么取于 length 只有 2 种结果,一种是 0,一种是1;
                            3、如果是结果是0 ,Doug Lea 将其放在低位,反之放在高位,目的是将链表重新 hash,放到对应的位置上,让新的取于算法能够击中他。*/
                            int runBit = fh & n;
                            Node<K,V> lastRun = f;
                            // 先遍历一遍这个桶,lastRun机制,并且计算runBit
                            for (Node<K,V> p = f.next; p != null; p = p.next) {
                                // 取于桶中每个节点的hash值
                                int b = p.hash & n;
                                // 如果节点的hash值和首节点hash值不相同
                                if (b != runBit) {
                                    // 更新runBit,用于判断lastRun该赋值给ln还是hn
                                    runBit = b;
                                    // lastRun用于保证后面的节点与自己的值相同,避免进行不必要的循环
                                    lastRun = p;
                                }
                            }
                            // 最后更新的runBit等0,赋值给低位桶
                            if (runBit == 0) {
                                ln = lastRun;
                                hn = null;
                            }
                            // runbit不等0,赋值给高位桶
                            else {
                                hn = lastRun;
                                ln = null;
                            }
                            // 再次循环产生两条链表,lastRun为停止条件
                            for (Node<K,V> p = f; p != lastRun; p = p.next) {
                                int ph = p.hash; K pk = p.key; V pv = p.val;
                                // 如果与运算结果为0
                                if ((ph & n) == 0)
                                    // 创建低位节点
                                    ln = new Node<K,V>(ph, pk, pv, ln);
                                else
                                    // 创建高位节点
                                    hn = new Node<K,V>(ph, pk, pv, hn);
                            }
                            // 设置低位节点放在新链表的i处
                            setTabAt(nextTab, i, ln);
                            // 设置高位节点放在新链表的i+n处
                            setTabAt(nextTab, i + n, hn);
                            // 设置旧链表为占位符
                            setTabAt(tab, i, fwd);
                            // 向后推进
                            advance = true;
                        }
                        // 判断是否为红黑树
                        else if (f instanceof TreeBin) {
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            TreeNode<K,V> lo = null, loTail = null;
                            TreeNode<K,V> hi = null, hiTail = null;
                            int lc = 0, hc = 0;
                            // 遍历红黑树
                            for (Node<K,V> e = t.first; e != null; e = e.next) {
                                // 下一节点hash值
                                int h = e.hash;
                                TreeNode<K,V> p = new TreeNode<K,V>
                                    (h, e.key, e.val, null, null);
                                // 和链表的判断相同,等0的放低位
                                if ((h & n) == 0) {
                                    if ((p.prev = loTail) == null)
                                        lo = p;
                                    else
                                        loTail.next = p;
                                    loTail = p;
                                    ++lc;
                                }
                                // 不等0,放高位
                                else {
                                    if ((p.prev = hiTail) == null)
                                        hi = p;
                                    else
                                        hiTail.next = p;
                                    hiTail = p;
                                    ++hc;
                                }
                            }
                            // 节点数<=6,转换为链表。否则转换为新的红黑树
                            ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
                                (hc != 0) ? new TreeBin<K,V>(lo) : t;
                            hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
                                (lc != 0) ? new TreeBin<K,V>(hi) : t;
                            // 低位节点
                            setTabAt(nextTab, i, ln);
                            // 高位节点
                            setTabAt(nextTab, i + n, hn);
                            // 旧表设置为占位符
                            setTabAt(tab, i, fwd);
                            // 继续推进
                            advance = true;
                        }
                    }
                }
            }
        }
    }

协助扩容helpTransfer

	final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
        Node<K,V>[] nextTab; int sc;
        // tab不为null且node节点为转移类型fwd且ndoe节点的下一个表(新表)不为空
        if (tab != null && (f instanceof ForwardingNode) &&
            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
            // 根据当前表的length得到一个标识符号(扩容标识戳)
            int rs = resizeStamp(tab.length);
            // nextTab还没有被修改且tab没被修改
            // sizeCtl < 0,说明正在扩容
            while (nextTab == nextTable && table == tab &&
                   (sc = sizeCtl) < 0) {
                // 如果 sizeCtl 无符号右移  16 不等于 rs ( sc前 16 位如果不等于标识符,则标识符变化了)
            	// 或者 sizeCtl == rs + 1  (扩容结束了,不再有线程进行扩容)(默认第一个线程设置 sc ==rs 左移 16 位 + 2,当第一个线程结束扩容了,就会将 sc 减一。这个时候,sc 就等于 rs + 1)
            	// 或者 sizeCtl == rs + 65535  (如果达到最大帮助线程的数量,即 65535)
            	// 或者转移下标正在调整 (扩容结束)
            	// 结束循环,返回 table
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
                    break;
                // 如果都不是,将sizeCtl + 1,表示增加了一个线程帮助扩容
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                    // 调用扩容方法进行转移
                    transfer(tab, nextTab);
                    // 结束循环
                    break;
                }
            }
            return nextTab;
        }
        return table;
    }

查找get()

	public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        // 拿到key的hash值
        int h = spread(key.hashCode());
        // 如果表不为空且表长度>0且key所在的桶不为空
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
            // 如果桶所在节点的hash值和key的hash值相同
            if ((eh = e.hash) == h) {
                // 判断当前key是否为桶的头节点
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    // 返回value值
                    return e.val;
            }
            // eh<0说明当前节点有两种可能,1、fwd节点;2、树节点
            else if (eh < 0)
                // 在桶中找到返回value值,否则返回空
                return (p = e.find(h, key)) != null ? p.val : null;
            // 所在桶的节点的下一个节点不为空,循环
            while ((e = e.next) != null) {
                // 如果节点的hash值和目标key的hash值相同且key相等
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    // 返回当前节点的value值
                    return e.val;
            }
        }
        return null;
    }

删除remove()

	public V remove(Object key) {
        return replaceNode(key, null, null);
    }
	final V replaceNode(Object key, V value, Object cv) {
        // 获取要删除的key的hash值
        int hash = spread(key.hashCode());
        // 把当前表赋值给tab
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            // 表不为空且长度不为0
            if (tab == null || (n = tab.length) == 0 ||
                // key的hash值所对应的位置节点是否为空
                (f = tabAt(tab, i = (n - 1) & hash)) == null)
                break;
            // 如果hash值为MOVED,说明有线程正在进行扩容
            else if ((fh = f.hash) == MOVED)
                // 帮助扩容
                tab = helpTransfer(tab, f);
            else {
                V oldVal = null;
                boolean validated = false;
                // 对当前节点进行加锁
                synchronized (f) {
                    // 如果i下标处的节点和f相同
                    if (tabAt(tab, i) == f) {
                        // 判断是否为链表
                        if (fh >= 0) {
                            validated = true;
                            // 将头部节点赋值给当前节点,自旋
                            for (Node<K,V> e = f, pred = null;;) {
                                K ek;
                                // 当前节点的hash值是否和要删除的key的hash值相同
                                if (e.hash == hash &&
                                    // key是否相同
                                    ((ek = e.key) == key ||
                                     // key不为空且值是否相等
                                     (ek != null && key.equals(ek)))) {
                                    // 当前节点的val值
                                    V ev = e.val;
                                    // 调用remove时这里的参数cv是null
                                    // 传入的cv 和当前节点值相等
                                    if (cv == null || cv == ev ||
                                        // 当前节点的val不为空且传入的cv和当前节点val值相同
                                        (ev != null && cv.equals(ev))) {
                                        // 将当前节点val值设置为旧节点
                                        oldVal = ev;
                                        // 如果传入的value不为空
                                        if (value != null)
                                            // 赋值给当前节点
                                            e.val = value;
                                        // 如果传入的pred不为空,即前置指针
                                        // 即当前节点不是第一个节点
                                        else if (pred != null)
                                            // 将当前节点的上一个节点的node.next指向当前节点的下一个节点,使当前节点没有被其他节点引用,最后被GC掉
                                            pred.next = e.next;
                                        else
                                            // 当前节点是头节点,那么就通过cas将头节点指向第二个节点,使头节点没有被其他节点引用,最后被GC掉
                                            setTabAt(tab, i, e.next);
                                    }
                                    break;
                                }
                                pred = e;
                                // 没有找到当前key对应的节点,继续往后遍历
                                if ((e = e.next) == null)
                                    break;
                            }
                        }
                        // 当前节点为红黑树
                        else if (f instanceof TreeBin) {
                            validated = true;
                            TreeBin<K,V> t = (TreeBin<K,V>)f;
                            TreeNode<K,V> r, p;
                            // 如果树节点的根不为空,且指定的key和hash不为空
                            if ((r = t.root) != null &&
                                (p = r.findTreeNode(hash, key, null)) != null) {
                                // 将当前节点的val值保存
                                V pv = p.val;
                                // 如果cv为null,或者传入cv值和找到的pv值相等
                                if (cv == null || cv == pv ||
                                    // 当前节点val不为空且val和传入的cv相同
                                    (pv != null && cv.equals(pv))) {
                                    // 将pv(p.val)赋值给旧值
                                    oldVal = pv;
                                    // 如果传入的value值不为null
                                    if (value != null)
                                        // 将value的值给到当前节点p的值
                                        p.val = value;
                                    // 如果能够删除当前节点p返回true
                                    else if (t.removeTreeNode(p))
                                        // 执行树转链表
                                        setTabAt(tab, i, untreeify(t.first));
                                }
                            }
                        }
                    }
                }
                // 如果有其他线程改变了头节点
                // 此时 validated 为 false,当前线程会进入下一个for循环
                // 而不会走这里面的退出逻辑 
                // 即validated是在synchrinized里面被改为true的,所以每次只有一个线程可以进入该逻辑
                if (validated) {
                    // 如果旧值为空说明table中有这个值,那么就删除成功了,则桶的node数减1
                    if (oldVal != null) {
                        // 如果传入的value为空
                        if (value == null)
                            // 将节点数量-1
                            addCount(-1L, -1);
                        // 返回旧值
                        return oldVal;
                    }
                    break;
                }
            }
        }
        return null;
    }	
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值