ConcurrentHashMap源码详解

总览

1、ConcurrentHashMap基础介绍

线程安全版本的hashmap,在多线程环境下通过加锁来保证线程安全,相比于同样线程安全的HashTable,有更细粒度的锁,提高并发性能;基础的API操作同HashMap一致,底层锁机制保证了并发环境下的线程安全特性。

2、源码相关重要内部类介绍

为了更好理解源码的细节,需要对源码关键API涉及到的内部类、属性、方法做前置的介绍;

2.1、内部类介绍:
static class Node<K,V> implements Map.Entry<K,V> {
		// node节点的hash值
        final int hash;
        // 节点对应的key
        final K key;
        // 节点对应的value
        volatile V val;
        // 保存后一个节点的引用
        volatile Node<K,V> next;
        Node(int hash, K key, V val, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.val = val;
            this.next = next;
        }
        public final K getKey()       { return key; }
        public final V getValue()     { return val; }
        public final int hashCode()   { return key.hashCode() ^ val.hashCode(); }
        public final String toString(){ return key + "=" + val; }
        // 不允许修改node节点的对应的值
        public final V setValue(V value) {
            throw new UnsupportedOperationException();
        }
		// 重写equals方法,key、value不为null,并且key和value与当前node相等。
        public final boolean equals(Object o) {
            Object k, v, u; Map.Entry<?,?> e;
            return ((o instanceof Map.Entry) &&
                    (k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
                    (v = e.getValue()) != null &&
                    (k == key || k.equals(key)) &&
                    (v == (u = val) || v.equals(u)));
        }
		// 传入一个hash值和对象K,如果当前对象的hash值与传入的h相等,并且key值与传入的K相等,
		// 则返回当前node对象,否则继续下一轮遍历,直到末尾,返回null,
		// 即查找指定hash码和相同key的节点并返回、不存在返回null
        Node<K,V> find(int h, Object k) {
            Node<K,V> e = this;
            if (k != null) {
                do {
                    K ek;
                    if (e.hash == h &&
                        ((ek = e.key) == k || (ek != null && k.equals(ek))))
                        return e;
                } while ((e = e.next) != null);
            }
            return null;
        }
    }

2.2、 属性介绍
// Node节点数组存放所有的元素
transient volatile Node<K,V>[] table;
// 用来控制初始化、扩容的状态标识,小于0代表当前对象正在扩容或者初始化,
// 为正数代表当前对象初始化后的大小
private transient volatile int sizeCtl;
2.3、方法介绍
// 1、initTable()用来初始化化table数组
private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
        // 判断当前的table数组为null,且或者长度为0时执行循环体
        while ((tab = table) == null || tab.length == 0) {
        //	如果当前对象状态sizeCtl小于0,则在处于初始化或者扩容状态
            if ((sc = sizeCtl) < 0)
            // 执行yield,让出cpu的执行权
                Thread.yield(); // lost initialization race; just spin
            // 此时如果没有其他线程在执行扩容和初始化,那么通过CAS操作将当前sizeCtl的值
            // 修改成-1,代表当前线程正在初始化;如果别的线程也进来将让出CPU因为此时已经成-1
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                // 再次判断table数组是否为null,或者长度为0
                    if ((tab = table) == null || tab.length == 0) {
                    // 如果sc有初始值,则按照给定初始值进行初始化,否则默认大小
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        // 创建指定大小的node数组
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                        // 进行赋值给当前对象table属性和tab
                        table = tab = nt;
                        // sc的大小变成原先成的3/4,即阈值变成新容量的0.75
                        sc = n - (n >>> 2);
                    }
                } finally {
                // 新容量的值赋值给状态标志
                    sizeCtl = sc;
                }
                break;
            }
        }
        // 返回,扩容过程结束
        return tab;
    }
// 2、casTabAt()方法cas判断当前tab对象该位置的对象为c,则更改为v
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);
}
// 3、helpTransfer()方法 帮助正在转移的线程进行数据转移
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
        Node<K,V>[] nextTab; int sc;
        if (tab != null && (f instanceof ForwardingNode) &&
            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
            int rs = resizeStamp(tab.length);
            while (nextTab == nextTable && table == tab &&
                   (sc = sizeCtl) < 0) {
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
                    break;
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                    transfer(tab, nextTab);
                    break;
                }
            }
            return nextTab;
        }
        return table;
    }
// 4、putTreeVal()红黑树结构的数据插入,返回TreeNode节点
```java
final TreeNode<K,V> putTreeVal(int h, K k, V v) {
            Class<?> kc = null;
            // 插入操作,查找标识为false
            boolean searched = false;
            // 红黑树根节点赋值给p,开始自旋
            for (TreeNode<K,V> p = root;;) {
                int dir, ph; K pk;
                // 若根节点为null,空树,则直接执行插入
                if (p == null) {
                	// 根据传入的hash,k,v初始化TreeNode节点,赋值给root和first,跳出自旋
                    first = root = new TreeNode<K,V>(h, k, v, null, null);
                    break;
                }
                // 如果头结点不为null,且头结点的hash值大于待传入的hash值
                else if ((ph = p.hash) > h)
                	// 令dir为-1
                    dir = -1;
                // 如果头结点hash值小于待传入hash值
                else if (ph < h)
                // 令dir=1,正负数来表示插入的数据大小
                    dir = 1;
                // 如果根节点的key和传入的key相等
                else if ((pk = p.key) == k || (pk != null && k.equals(pk)))
                	// 直接返回跟元素
                    return p;
                // kc为null,comparableClassFor(k)返回传入key的比较器类型赋值给kc为null
                // 或compareComparables(kc, k, pk))比较k和pk的大小,根据kc比较器规则为0
                // 上述条件都属于不能够按照key比较排序的情形,要么传入空比较器,要么就是两个
                // key没有实现同一比较器类型,或者头部的pk为null
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0) {
                    // 插入操作,进来的时候,search已经置为false
                    if (!searched) {
                    	// 声明q,ch节点,改search为true值
                        TreeNode<K,V> q, ch;
                        searched = true;
                        // 获取根节点的左子节点赋值给ch,不为null
                        // ch.findTreeNode()从左子节点开始遍历找到h,k相等的节点,找不到则为null
                        // 如果在左子树找到了或者在右子树找到了,那么就直接返回该节点。
                        if (((ch = p.left) != null &&
                             (q = ch.findTreeNode(h, k, kc)) != null) ||
                            ((ch = p.right) != null &&
                             (q = ch.findTreeNode(h, k, kc)) != null))
                            return q;
                    }
                    // 直接比较传入的k和根部节点的pk,比较hash值,如果小于pk,返回-1,否则返回1
                    dir = tieBreakOrder(k, pk);
                }
				// 走到这意味上面没有找到指定的节点,将根节点赋值给xp
                TreeNode<K,V> xp = p;
                // 如果dir小于0,将左节点给p,否则右节点给p,如果等于null执行if逻辑
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                	// 根节点first赋值给f
                    TreeNode<K,V> x, f = first;
                    // 初始化节点赋值给x和first节点
                    first = x = new TreeNode<K,V>(h, k, v, f, xp);
                    if (f != null)
                    // f不为null,将x节点的值赋值给f节点前置指针指向x
                        f.prev = x;
                    if (dir <= 0)
                    // 如果dir<0,将当前x作为父节点的左孩子,否则为右孩子
                        xp.left = x;
                    else
                        xp.right = x;
                    // 如果父节点为黑色,将父节点改为红色
                    if (!xp.red)
                        x.red = true
                    else {
                    	// 如果为红色,则锁定树,将锁定状态改为写入模式
                        lockRoot();
                        try {
                        	// 开始调整红黑树结构,此处不展开,参考上一篇hashmap红黑树调整的逻辑处理
                            root = balanceInsertion(root, x);
                        } finally {
                        	// 解除锁定,更改lockState=0
                            unlockRoot();
                        }
                    }
                    break;
                }
            }
            // 验证红黑树的准确性,判断父节点和左右子节点的指向是否满足规则,色块是否满足红黑树规则
            assert checkInvariants(root);
            // 最后执行到这,插入成功,直接返回null
            return null;
        }

3、源码部分详解

下面对于ConcurrentHashMap的介绍从常用的API入手,包括增加、删除、更新、查询、扩容操作。

3.1 put() 添加元素
源码部分对key和value都标注了@NotNull注解,
表示put操作不允许null key和null value
public V put(K key, V value) {
        return putVal(key, value, false);
}
继续调用putVal()方法
final V putVal(K key, V value, boolean onlyIfAbsent) {
       	// 判断key或者value为null值,直接抛出空指针异常
        if (key == null || value == null) throw new NullPointerException();
        // hashcode方法作用,获取key对应的hash码,属于native方法
        // spread方法作用,将key对应的hash码高16位和低16位进行异或,结果与HASH_BITS按位与
        // 该做法保证最后的结果是正数,hash_bits=0x7fffffff,保证最高位结果一定是0也就是一定为正
        int hash = spread(key.hashCode());
        // 用来记录插入的个数
        int binCount = 0;
        // 当前table数组赋值给tab的node数组
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            // 如果tab数组位null,或者数组长度为0,那么执行初始化
            if (tab == null || (n = tab.length) == 0)
            // 进行初始化,初始化细节见2.3部分
                tab = initTable();
            // 如果不为空,并且当前hash值对应的数组位置的元素f不为null
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            // cas判断i位置头部元素四是否为null,是则修改为一个Node元素;
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            // 如果该位置不为空,则判断该元素的hash值是否为MOVED=-1,是则表示正在转移
            else if ((fh = f.hash) == MOVED)
            // 则帮助进行数据转移
                tab = helpTransfer(tab, f);
            else {
            // 声明酒值为null
                V oldVal = null;
                // 头部节点加锁
                synchronized (f) {
                // 再次判断i位置的头部节点是否为f
                    if (tabAt(tab, i) == f) {
                    	// f头结点的hash值大于0
                        if (fh >= 0) {
                        // 插入的个数变成1
                            binCount = 1;
                            // 头结点赋值给e,个数累加
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                // 待插入hash值和当前遍历节点e的hash值相等,且key相等
 								// 存在相同key,则是一次更新操作
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                     // 将当前的value值赋值给旧值,因为要完成一个更新操作
                                    oldVal = e.val;
                                    // 此时传入的是false,那么允许更新
                                    // onlyIfAbsent字面意思是仅不存在更新,true的话将不更新操作,
                                    // 只有在false的时候,代表存在也可以更新
                                    if (!onlyIfAbsent)
                                        e.val = value;
                                    break;
                                }
                                // 走到这儿代表不存在相同key,保留当前节点e赋值给pred
                                Node<K,V> pred = e;
                                // e向下寻找next,为空的话,则以传入的hash,key,value初始
                                // 化一个node节点,放在pred的后面,可见是尾部插入
                                if ((e = e.next) == null) {
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        // 如果是头节点的类型属于树类型,那么此时为红黑树结构
                        else if (f instanceof TreeBin) {
                        // f头节点属于树类型,将binCount的值改为2,存在一个和待插入一个
                            Node<K,V> p;
                            binCount = 2;
                            // 执行TreeBin的putTreeVal方法,如果返回值不为null,代表找到了,相同key
                            // 的节点,则返回该节点的value值,并赋值给oldVal,
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                oldVal = p.val;
                                // 此时传入为false,更新红黑树结构对应节点的value值
                                if (!onlyIfAbsent)
                                    p.val = value;
                            }
                        }
                    }
                }
                // binCount记录了遍历了的节点数,如果节点数不为0
                if (binCount != 0) {
                	// 判断数量是否大于树化阈值8,如果满足
                    if (binCount >= TREEIFY_THRESHOLD)
                    	// tab数组,从i位置开始进行树化,判断是否满足最小树化的容量标注
                    	// 对该位置加锁进行树形化,
                        treeifyBin(tab, i);
                    if (oldVal != null)
                    	// 如果旧值不为null,返回旧值,所以插入操作,会返回旧的值
                        return oldVal;
                    break;
                }
            }
        }
        // 插入一个元素,是否要选择扩容;
        addCount(1L, binCount);
        return null;
    }
3.2、remove删除元素
源码对于key标识了@NotNull注解,表示要删除的key不能为null
public V remove(Object key) {
        return replaceNode(key, null, null);
    }
继续调用replaceNode()方法
final V replaceNode(Object key, V value, Object cv) {
		// 获取key的hash码
        int hash = spread(key.hashCode());
        // 自旋,将table数组赋值给tab
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            // tab为空,或者长度为0,hash值所对应位置的头部节点为空的情况跳出循环
            if (tab == null || (n = tab.length) == 0 ||
                (f = tabAt(tab, i = (n - 1) & hash)) == null)
                break;
            // 如果头结点的hash值表示正在转移,则当前线程帮助转移数据
            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) {
                    	// 头节点hash值大于0
                        if (fh >= 0) {
                            validated = true;
                            //	node头部节点赋值给e,前置节点pred为null,自旋
                            for (Node<K,V> e = f, pred = null;;) {
                                K ek;
                                // 如果找到hash和key相等的当前节点e
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                     // 当前节点的value值给到ev
                                    V ev = e.val;
                                    // 此时传入的cv为null,或者传入的cv和当前节点的value值相等
                                    // 或者ev不为null,cv和ev相等
                                    if (cv == null || cv == ev ||
                                        (ev != null && cv.equals(ev))) {
                                        // 把ev的值给到旧值oldVal
                                        oldVal = ev;
                                        // 如果传入的value不为null,将当前节点的value值改为传入值
                                        if (value != null)
                                            e.val = value;
                                        // 如果前置指针不为null,则将当前节点拼在前置节点后面
                                        // 这里的节点删除了。    
                                        else if (pred != null)
                                            pred.next = e.next;
                                        else
                                        // 如果传入的为null,pred也为null,
                                        // cas将头节点设置为下一个
                                            setTabAt(tab, i, e.next);
                                    }
                                    break;
                                }
                                // 没有找到当前key对应的节点,继续往后遍历
                                pred = e;
                                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;
                            // 根不为空,指定hash和key的节点存在不为空的情况下,将当前节点的值用
                            // pv存下来
                            if ((r = t.root) != null &&
                                (p = r.findTreeNode(hash, key, null)) != null) {
                                V pv = p.val;
                                // 如果cv为null,或者传入cv值和找到的pv值相等,将pv给旧的值
                                if (cv == null || cv == pv ||
                                    (pv != null && cv.equals(pv))) {
                                    oldVal = pv;
                                    // 如果传入的value值不为null,将value的值给到当前节点p的值
                                    if (value != null)
                                        p.val = value;
                                    // 如果能够删除当前节点p返回true,则在i位置执行反树化,
                                    else if (t.removeTreeNode(p))
                                        setTabAt(tab, i, untreeify(t.first));
                                }
                            }
                        }
                    }
                }
                if (validated) {
                    if (oldVal != null) {
                        if (value == null)
                        // 传入-1,将count树减一,返回旧值,
                            addCount(-1L, -1);
                        return oldVal;
                    }
                    break;
                }
            }
        }
        // 如果没找到旧的值,直接返回null
        return null;
    }
3.3、get获取元素
传入key,获取指定key对应的value值。获取元素过程通过cas机制,并未通过加锁控制,
这一点比JDK1.7的加锁实现性能上要好很多
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());
        // tab数组不为空,指定hash值对应位置的头部节点不为null
        // tabAt()通过cas机制判断i位置的内存偏移量是否发生变更。来获取节点值
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) {
            // 如果hash值和头部节点的hash值相等
            if ((eh = e.hash) == h) {
            	// 判断key是否于当前节点的key相等,相等则返回该节点对应的value值
                if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                    return e.val;
            }
            // 如果hash值小于0,则说明此时链表处于转移的情况,如果是-1则当前头节点是ForwardingNode
            // 那么通过forwardingNode去访问nextTable去找元素。如果是正在树化,则需要从TreeBin里
            // 找这个元素,如果hash>0则是正常的链表去逐个遍历就行。
            else if (eh < 0)
            	// find()从e开始向下遍历链表找到指定的key和hash的节点给p,如果不为null,返回节点p
            	// 的value值,否则返回null
                return (p = e.find(h, key)) != null ? p.val : null;
            // 没找到相等key的,继续向后遍历e.next,不为空的话判断 hash和key是否相等,
            // 相等返回当前节点的value值,否则,继续while向后遍历    
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek))))
                    return e.val;
            }
        }
        // 最后遍历结束,找不到就返回null
        return null;
    }
3.4、扩容transfer()、helpTransfer()相关方法
帮助转移数据,tab数组当前待转移的数组,f当前桶的头节点
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
        Node<K,V>[] nextTab; int sc;
        // tab不为null,并且头节点是转移标识
        if (tab != null && (f instanceof ForwardingNode) &&
            (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
            int rs = resizeStamp(tab.length);
            while (nextTab == nextTable && table == tab &&
                   (sc = sizeCtl) < 0) {
                if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
                    sc == rs + MAX_RESIZERS || transferIndex <= 0)
                    break;
                // CAS 将当前线程去执行数据转移,将sc的数值+1表示当前执行转移的线程数+1    
                if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) {
                    transfer(tab, nextTab);
                    break;
                }
            }
            return nextTab;
        }
        return table;
    }
下面继续分析transfer()方法源码部分
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
        int n = tab.length, stride;
        // 转移区块的数量MIN_TRANSFER_STRIDE=16 如果小于16则按照16来,不然就根据CPU数量来
        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
            stride = MIN_TRANSFER_STRIDE; // subdivide range
        // 如果nextTab为null,则将其初始化,长度为当前数组长度n的2倍
        if (nextTab == null) {            // initiating
            try {
                @SuppressWarnings("unchecked")
                Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
                nextTab = nt;
            } catch (Throwable ex) {      // try to cope with OOME
                sizeCtl = Integer.MAX_VALUE;
                return;
            }
            // 那么可以将nextTab的引用传递给全局的nextTable属性
            nextTable = nextTab;
            // 转移索引置为n
            transferIndex = n;
        }
        // 新数组的长度nextn
        int nextn = nextTab.length;
        // 构造转移标识节点fwd
        ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
        // 当advance == true时,表明该节点已经处理过了 
        boolean advance = true;
        boolean finishing = false; // to ensure sweep before committing nextTab
        for (int i = 0, bound = 0;;) {
            Node<K,V> f; int fh;
            while (advance) {
                int nextIndex, nextBound;
                //迁移总进度<=0,表示所有桶都已迁移完成
                if (--i >= bound || finishing)
                    advance = false;
                else if ((nextIndex = transferIndex) <= 0) {
                    i = -1;
                    advance = false;
                }
                // 下一个索引位置大于区块末尾,则nextIndex减去前一个区块,否则为0
                // cas判断TRANSFERINDEX是否于nextIndex相等,是则将其按照上面规则调整
                else if (U.compareAndSwapInt
                         (this, TRANSFERINDEX, nextIndex,
                          nextBound = (nextIndex > stride ?
                                       nextIndex - stride : 0))) {
                    //确定当前线程每次分配的待迁移桶的范围为[bound, nextIndex)                  
                    bound = nextBound;
                    i = nextIndex - 1;
                    advance = false;
                }
            }
            if (i < 0 || i >= n || i + n >= nextn) {
                int sc;
                // 扩容结束,标志为true,将nextTable属性改为null,nextTab赋值给全局table,
                // sizeCtl属性2n-0.5n=1.5n 也就是2n*0.75=1.5n 也就是扩容后的容量阈值
                if (finishing) {
                    nextTable = null;
                    table = nextTab;
                    sizeCtl = (n << 1) - (n >>> 1);
                    return;
                }
                // CAS判断SIZECTL是否为sc,如果是将其减少1,将状态更改为完成
                if (U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
                	// 如果没完成,则继续
                    if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
                        return;
                    // 当前线程转移完成,更改状态
                    finishing = advance = true;
                    i = n; // recheck before commit
                }
            }
            //如果i处是ForwardingNode表示第i个桶已经有线程在负责迁移了
            else if ((f = tabAt(tab, i)) == null)
                advance = casTabAt(tab, i, null, fwd);
            else if ((fh = f.hash) == MOVED)
                advance = true; // already processed
            else {
            // 对头部节点加锁控制,避免转移过程,有put操作冲突
                synchronized (f) {
                    if (tabAt(tab, i) == f) {
                        Node<K,V> ln, hn;
                        // hash值大于0,代表链表节点
                        if (fh >= 0) {
                        // 构造两个链表  一个是原链表  另一个是原链表的反序排列
                            int runBit = fh & n;
                            Node<K,V> lastRun = f;
                            for (Node<K,V> p = f.next; p != null; p = p.next) {
                                int b = p.hash & n;
                                if (b != runBit) {
                                    runBit = b;
                                    lastRun = p;
                                }
                            }
                            if (runBit == 0) {
                                ln = lastRun;
                                hn = null;
                            }
                            else {
                                hn = lastRun;
                                ln = null;
                            }
                            for (Node<K,V> p = f; p != lastRun; p = p.next) {
                                int ph = p.hash; K pk = p.key; V pv = p.val;
                                if ((ph & n) == 0)
                                    ln = new Node<K,V>(ph, pk, pv, ln);
                                else
                                    hn = new Node<K,V>(ph, pk, pv, hn);
                            }
                            // 在nextTable i位置处插上链表 
                            setTabAt(nextTab, i, ln);
                            // 在nextTable i + n 位置处插上链表
                            setTabAt(nextTab, i + n, hn);
                            // 在table i 位置处插上ForwardingNode 表示该节点已经处理过了 
                            setTabAt(tab, i, fwd);
                            // advance = true 可以执行--i动作,遍历节点 
                            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) {
                                int h = e.hash;
                                TreeNode<K,V> p = new TreeNode<K,V>
                                    (h, e.key, e.val, null, null);
                                if ((h & n) == 0) {
                                    if ((p.prev = loTail) == null)
                                        lo = p;
                                    else
                                        loTail.next = p;
                                    loTail = p;
                                    ++lc;
                                }
                                else {
                                    if ((p.prev = hiTail) == null)
                                        hi = p;
                                    else
                                        hiTail.next = p;
                                    hiTail = p;
                                    ++hc;
                                }
                            }
                            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;
                        }
                    }
                }
            }
        }
    }

总结:
1、JDK1.8采用了更细粒度的锁,采用Node+Synchronized+CAS算法,来替代JDK1.7 Reentrantlock+Segment机制,降低了锁粒度,并发性能更好,并且结构上也与hashmap更加相似
2、使用Synchronized关键字比使用api级别的更加流畅,并且对关键字已经做了优化;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值