ConcurrentHashMap 源码分析记录(JDK1.8)

ConcurrentHashMap 源码分析记录

ConcurrentHashMap继承了AbstractMap<K,V>,并实现了ConcurrentMap<K,V>, Serializable 接口

1、源码分析
A、成员变量
//最大容量
private static final int MAXIMUM_CAPACITY = 1 << 30;

//默认初始容量
 private static final int DEFAULT_CAPACITY = 16;

//最大数组容量,减8的原因是预留类加载内存空间
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 UNTREEIFY_THRESHOLD = 6;

//扩容时调用多个线程帮助扩容,每个线程分到的最小任务量
private static final int MIN_TRANSFER_STRIDE = 16;

//该四个变量为Node对象的hash变量值
	static final int MOVED     = -1; // hash for forwarding nodes
    static final int TREEBIN   = -2; // hash for roots of trees
    static final int RESERVED  = -3; // hash for transient reservations
    static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash

//当前机器的CPU数
 static final int NCPU = Runtime.getRuntime().availableProcessors();

//存储Node对象的数组

jdk8中ConcurrentHashMap共有5个构造方法,构造方法都没有对内部的数组做初始化,只是对一些变量的初始值做了处理,

jdk8的ConcurrentHashMap的数组初始化是在第一次添加元素时完成的

public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
    implements ConcurrentMap<K,V>, Serializable {
}
//没有维护任何变量的操作,如果调用该方法,则默认数组长度为16
public ConcurrentHashMap() {
    }
//传递进来一个自定义的容量大小,ConcurrentHashMap会基于这个容量参数算出一个比该值大的最小2的幂次方作为数组的初始容量
public ConcurrentHashMap(int initialCapacity) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException();
    //初始容量的计算:先判断传入的值是否大于最大容量的一半,大于则初始容量设为最大容量的一半
        int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
                   MAXIMUM_CAPACITY :
                   //否则传入参数:传入参数+传入参数的右移1位+1进行移位运算得到初始容量,如传入15,则进行移位运算的参数为15+7+1 = 23,那么移位运算得到的大于该参数的最小的2的幂次方,大于23的最小幂次方为32
                   tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
    //将初始容量赋值给sizeCtl
        this.sizeCtl = cap;
    }

注意:调用这个方法,即使传入的是2的幂次方数,得到的初始容量也比该数大,与jdk7不同。根据当前集合的预计长度(元素个数),建议给一个初始容量进行实例构造,以免后期频繁扩容,影响性能


//初始化一个传入Map集合的ConcurrentHashMap 
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
    //将默认容量赋值给sizeCtl
        this.sizeCtl = DEFAULT_CAPACITY;
    //将传入的集合所有元素放到hashmap中
        putAll(m);
    }

//传入自定义的容量和加载因子
public ConcurrentHashMap(int initialCapacity, float loadFactor) {
        this(initialCapacity, loadFactor, 1);
    }

//构造自定义的容量和加载因子及预估的并发线程数量的实例
//initialCapacity初始容量。
//loadFactor用于建立初始表大小的负载因子(表密度)
//concurrencyLevel并发更新线程的估计数量。
public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (initialCapacity < concurrencyLevel)   // Use at least as many bins
            initialCapacity = concurrencyLevel;   // as estimated threads
    //传入的参数进行加工
        long size = (long)(1.0 + (long)initialCapacity / loadFactor);
        int cap = (size >= (long)MAXIMUM_CAPACITY) ?
            MAXIMUM_CAPACITY : tableSizeFor((int)size);
        this.sizeCtl = cap;
    }
//值为0:代表数组未初始化,且初始容量为16
//值为正数:1、若数组未初始化,则值为数组的初始容量;2、若数组已经初始化,则值为数组的扩容阈值
//值为-1:代表数组正在进行初始化
//值小于0且不为-1,表示数组正在扩容,-(1+n),表示此时有n个线程正在共同完成扩容操作
private transient volatile int sizeCtl;
//put方法
public V put(K key, V value) {
        return putVal(key, value, false);
    }

    /** Implementation for put and putIfAbsent */
//实现元素添加的方法,不允许空值空键
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        //空值空键判断
        if (key == null || value == null) throw new NullPointerException();
        //通过键的hashcode方法来计算扰动,减少hash冲突,得到一个hash值,该方法得到的hash值一定为正数
        //该值一定为正数,方便后面添加节点时判断该节点的类型
        int hash = spread(key.hashCode());
        
        int binCount = 0;
        //进入死循环
        for (Node<K,V>[] tab = table;;) {
            Node<K,V> f; int n, i, fh;
            //判断对象数组是否为空及数组的容量是否为0
            if (tab == null || (n = tab.length) == 0)
                //初始化对象数组
                tab = initTable();
            //不为空则通过hash值与上数组容量-1得到一个下标,如果该下标的元素为空,则新建一个Node对象放到				该下标位置
            //tabAt方法是调用Unsafe的本地方法原子性的判断该索引位置是否为空
            else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
                //下标为空的Cas计算方法
                //通过CAS计算并将新的Node对象放到数组对应的下标处
                if (casTabAt(tab, i, null,
                             new Node<K,V>(hash, key, value, null)))
                    break;                   // no lock when adding to empty bin
            }
            //如果算出的索引位置有元素,并且该位置元素的hash值等于MOVED,则说明该数组正在扩容
            else if ((fh = f.hash) == MOVED)
                //调用协助扩容方法进行扩容
                tab = helpTransfer(tab, f);
            //既不为空也不在扩容,说明该位置有链表或是红黑树,则进入下面步骤进行元素添加
            else {
                V oldVal = null;
                //对当前数组索引位置的元素进行加锁,即对链表或树的头结点加锁
                synchronized (f) {
                    //这里的判断是防止当前位置的元素添加完成之后进行树化,则当前位置的头节点会改变
                    //如果元素还是一样,则说明该位置并没有进行树化或者数组没有进行扩容
                    if (tabAt(tab, i) == f) {
                        //如果该节点的hash值大于等于0,则说明该节点为链表
                        if (fh >= 0) {
                            //binCunt为记录链表的元素个数
                            binCount = 1;
                            //遍历链表,将新元素加到链表尾部
                            for (Node<K,V> e = f;; ++binCount) {
                                K ek;
                                //如果该索引位置的元素的hash值=传入的键的hash值,则说明键相同,替换										value值为新value值		
                                if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                     (ek != null && key.equals(ek)))) {
                                    //存储原来的value值
                                    oldVal = e.val;
                                    if (!onlyIfAbsent)
                                        //替换为新的value值
                                        e.val = value;
                                    break;
                                }
                                //将索引位置处的元素赋值给pred
                                Node<K,V> pred = e;
                                //如果该节点的后节点为空
                                if ((e = e.next) == null) {
                                    //将后节点设为传入的键值对Node对象
                                    pred.next = new Node<K,V>(hash, key,
                                                              value, null);
                                    break;
                                }
                            }
                        }
                        //如果该节点的hash值为负数,则为红黑树的节点
                        else if (f instanceof TreeBin) {
                            Node<K,V> p;
                            binCount = 2;
                            //调用putTreeVal方法对传入的键值进行树节点的判断和添加
                            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                                           value)) != null) {
                                //添加或查找成功,则记录原value
                                oldVal = p.val;
                                if (!onlyIfAbsent)
                                    //如果有相同的键,则替换value
                                    p.val = value;
                            }
                        }
                    }
                }
                if (binCount != 0) {
                    //如果binCount大于等于8,则进入树化方法
                    if (binCount >= TREEIFY_THRESHOLD)
                        treeifyBin(tab, i);
                    //如果有相同键,则返回被替换的value
                    if (oldVal != null)
                        return oldVal;
                    break;
                }
            }
        }
       //维护hashmap的元素个数
        addCount(1L, binCount);
        return null;
    }
//hash值的扰动算法,HASH_BITS的值为0x7fffffff,二进制0111 1111 1111 1111 1111 1111 1111 1111,最高位为0,与运算任何数都为0,因此得到的数一定为正数
 static final int spread(int h) {
        return (h ^ (h >>> 16)) & HASH_BITS;
    }
//初始化数组的方法
private final Node<K,V>[] initTable() {
        Node<K,V>[] tab; int sc;
    //数组为空且容量为0
    //CAS+自旋,保证线程安全,对数组进行初始化操作	
        while ((tab = table) == null || tab.length == 0) {
            //此时sizeCtl为初始化的0
            if ((sc = sizeCtl) < 0)
                //如果一个线程拿到了cpu执行权,则放弃,因为此时已有其他线程正在进行初始化
                Thread.yield(); // lost initialization race; just spin
            //通过CAS方法将sc设为-1,以便后面线程进来的时候进行放弃执行权的判断
            else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
                try {
                    //再次进行判断,doublechecklock,双检机制
                    if ((tab = table) == null || tab.length == 0) {
                        //此时sc为-1,因此n为默认容量
                        int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
                        @SuppressWarnings("unchecked")
                        //创建一个Node数组对象,容量为n
                        Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
                       //将创建好的数组对象赋值给table
                        table = tab = nt;
                        //对n进行移位运算得到12
                        sc = n - (n >>> 2);
                    }
                } finally {
                    //将sc的值赋给sizeCtl,因此此时sizeCtl的值为map的扩容阈值
                    //初始化完成后,sizeCtl为扩容阈值
                    sizeCtl = sc;
                }
                break;
            }
        }
        return tab;
    }

//维护集合长度的方法
 private final void addCount(long x, int check) {
     //x为1,
        CounterCell[] as; long b, s;
     //判断counterCells是否为空或者通过cas方法判断x能否加成功
        if ((as = counterCells) != null ||
            !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
            CounterCell a; long v; int m;
            boolean uncontended = true;
            //如果counterCells数组为空
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
                !(uncontended =
                  U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
                //进入方法进行数组的value值的计算
                fullAddCount(x, uncontended);
                return;
            }
            if (check <= 1)
                return;
            //获取集合元素个数
            s = sumCount();
        }
     //添加时check一定大于等于0
        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);
                //sc小于0,说明sizeCtl小于0
                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);
                }
                //此处将SIZECTL设置为-1,因为rs向左移17位后最高位变为了1,
                else if (U.compareAndSwapInt(this, SIZECTL, sc,
                                             (rs << RESIZE_STAMP_SHIFT) + 2))
                    //同样会进入扩容方法
                    transfer(tab, null);
                s = sumCount();
            }
        }
    }

//集合元素的个数
 final long sumCount() {
     //对baseCount和counterCells数组中的值进行累加,得到集合中的元素个数
        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种情况:添加第一个元素时,CounterCell[]数组为空,则会进入最下面的分支,对baseCount进行++操作,操作成功,则break;如果添加不成功,则会进入中间的分支,创建一个CounterCell[]数组,并初始化长度为2,如果原有数组不为空的话就扩容,扩容的最大值为cpu数,并进行元素copy;如果CounterCell[]数组不为空,则会维护CounterCell[]中CounterCell对象的value值,将添加的x值放到对象中
private final void fullAddCount(long x, boolean wasUncontended) {
    //x是上面方法传入的1,wasUncontended为true
        int h;
    //计算数组的随机下标
        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;
            //数组有值,说明有多线程在操作
            if ((as = counterCells) != null && (n = as.length) > 0) {
                //数组的下标处值为空
                if ((a = as[(n - 1) & h]) == null) {
                    if (cellsBusy == 0) {            // Try to attach new Cell
                        //新建一个CounterCell对象
                        CounterCell r = new CounterCell(x); // Optimistic create
                        //将cellsBusy又设为1
                        if (cellsBusy == 0 &&
                            U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                            
                            boolean created = false;
                            try {               // Recheck under lock
                                CounterCell[] rs; int m, j;
                                if ((rs = counterCells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {
                                    //将新建的CounterCell对象放到数组中
                                    rs[j] = r;
                                    created = true;
                                }
                            } finally {
                                cellsBusy = 0;
                            }
                            if (created)
                                break;
                            continue;           // Slot is now non-empty
                        }
                    }
                    collide = false;
                }
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                else if (U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))
                    break;
                //判断是否再进行扩容
                else if (counterCells != as || n >= NCPU)
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
                //如果cellsBusy为0,则通过CAS操作将其设为1,保证线程安全
                else if (cellsBusy == 0 &&
                         U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                    try {
                        if (counterCells == as) {// Expand table unless stale
                            //新建一个CounterCell数组,容量为n值左移1位
                            CounterCell[] rs = new CounterCell[n << 1];
                            //遍历数组,并将counterCells数组元素赋给该数组
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            counterCells = rs;
                        }
                    } finally {
                        //重新将该值设为0
                        cellsBusy = 0;
                    }
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                //重新计算随机值
                h = ThreadLocalRandom.advanceProbe(h);
            }
            //如果baseCount没有加成功,则创建CounterCell[]数组,并进入自旋
            else if (cellsBusy == 0 && counterCells == as &&
                     U.compareAndSwapInt(this, CELLSBUSY, 0, 1)) {
                boolean init = false;
                try {                           // Initialize table
                    if (counterCells == as) {
                        CounterCell[] rs = new CounterCell[2];
                        rs[h & 1] = new CounterCell(x);
                        counterCells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            }
            //baseCount加成功的情况
            else if (U.compareAndSwapLong(this, BASECOUNT, v = baseCount, v + x))
                break;                          // Fall back on using base
        }
    }


维护数组长度时,map集合有一个baseCount变量,首先对该变量进行操作,每添加一个元素,该值就加1,如果因为多线程没有+1成功,则会设置一个新的数组CounterCel[] counterCels,里面的counterCel对象有一个属性value,数组初始容量为2,会计算一个数组下标,将传入的x值加到下标位置的对象value值上,如果线程核数大于数组初始值,会进行数组扩容,并把数组中的对象copy到新数组中,扩容的量最多为cpu核数,最后通过sumCount()方法得到集合中的元素个数

在这里插入图片描述

扩容方法
//扩容方法,其原理是先判断cpu的线程数,然后确保每个线程的最小任务量为16,再新建一个数组,容量为原数组的2倍;
//又新建一个ForwardingNode对象,作为扩容过程的标识对象,并把新建的数组传给该对象,ForwardingNode对象里的hash值为MOVED,当数据迁移时,每迁移一个元素,就会新建一个ForwardingNode对象放在原数组原位置,标识这是一个正在迁移的集合,因此在前面进行put时会判断索引位置的元素的hash值,如果hash值为MOVED,表示数组正在扩容,线程就会去协助扩容
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
        int n = tab.length, stride;
    //计算每个线程协助迁移数据的任务量
    //每个线程最小任务量为16
        if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
            stride = MIN_TRANSFER_STRIDE; // subdivide range
        if (nextTab == null) {            // initiating
            try {
                @SuppressWarnings("unchecked")
                //新建一个数组,容量为原来的2倍
                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;
            }
            nextTable = nextTab;
          
            transferIndex = n;
        }
        int nextn = nextTab.length;
    //新建一个fwd对象,传入新的数组
        ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
        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;
                if (--i >= bound || finishing)
                    advance = false;
                else if ((nextIndex = transferIndex) <= 0) {
                    i = -1;
                    advance = false;
                }
                else if (U.compareAndSwapInt
                         (this, TRANSFERINDEX, nextIndex,
                          nextBound = (nextIndex > stride ?
                                       nextIndex - stride : 0))) {
                    bound = nextBound;
                    i = nextIndex - 1;
                    advance = false;
                }
            }
            if (i < 0 || i >= n || i + n >= nextn) {
                int sc;
                if (finishing) {
                    nextTable = null;
                    table = nextTab;
                    sizeCtl = (n << 1) - (n >>> 1);
                    return;
                }
                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
                }
            }
            //如果当前位置为空,则直接塞一个fwd对象
            else if ((f = tabAt(tab, i)) == null)
                advance = casTabAt(tab, i, null, fwd);
            //如果当前位置的元素hash值为MOVED,则表示迁移过了
            else if ((fh = f.hash) == MOVED)
                advance = true; // already processed
            //否则进入真正的迁移动作
            else {
                //对要迁移的位置加锁,对头节点加锁
                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;
                            }
                            //遍历链表,将与运算计算的值为0的设为低位链表,不为0的设为高位链表
                            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);
                            }
                            //低位链表设置为与原数组相同的索引,高位链表设置为原数组索引位置+原数组容量								位置	
                            setTabAt(nextTab, i, ln);
                            setTabAt(nextTab, i + n, hn);
                            //迁移完,将该位置元素设为fwd对象
                            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) {
                                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;
                                }
                            }
                            //树的迁移与HashMap逻辑类似
                            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;
                        }
                    }
                }
            }
        }
    }

扩容的原理:多个线程操作时,会从数组的尾部给每个线程分配任务,最小任务量是16,每个线程做完自己的任务后还会循环领任务,如果任务都做完了,最后做完的线程会退出扩容操作。扩容时,会给当前遍历到的桶位加锁,避免其他线程对该桶位进行其他操作,将数据迁移完成后,会在该桶位放一个fwd对象,其hash值为MOVED,用以在put时判断当前桶位的状态。JDK1.8中ConcurrentHashMap的优化就在于取代了1.7中分段锁的机制,进一步将锁的粒度缩小到每个桶位,这样,其他桶位在进行操作时不会受到影响,效率大大提高。

ConcurrentHashMapJava中并发访问的哈希表实现,它在多线程环境下提供了高效的并发操作。 在JDK 1.8中,ConcurrentHashMap的实现基于数组和链表结构,同时引入了红黑树来提高性能。下面是对ConcurrentHashMap源码解析: 1. 分段锁:ConcurrentHashMap中使用了分段锁(Segment)的机制,将整个数据结构分成多个Segment。每个Segment维护了一部分键值对,它们之间是相互独立的。这样在并发访问时,只需要锁住对应的Segment,不同的Segment可以并发执行,极大地提高了并发访问的效率。 2. 数据结构:ConcurrentHashMap内部使用了一个由Segment数组组成的table来存储数据。每个Segment都是一个独立的哈希表,继承自ReentrantLock来保证线程安全。每个Segment中包含一个HashEntry数组,每个HashEntry是一个链表或红黑树的节点。 3. put操作:当进行put操作时,首先计算键的哈希值,然后通过哈希值的高位和Segment数组长度进行运算,确定需要操作的Segment。在对应的Segment中进行插入操作,使用lock()方法获取Segment对应的锁。如果插入时发现链表过长(默认阈值为8),会将链表转换为红黑树,提高插入和查找的速度。如果插入的键已存在,会更新对应的值。 4. get操作:当进行get操作时,也首先计算键的哈希值,然后确定需要操作的Segment。在对应的Segment中进行查找操作,使用lock()方法获取Segment对应的锁。在链表或红黑树中查找键对应的值。 5. remove操作:当进行remove操作时,同样需要计算键的哈希值,确定需要操作的Segment。在对应的Segment中进行删除操作,使用lock()方法获取Segment对应的锁。在链表或红黑树中查找键,并删除对应的节点。 总结来说,ConcurrentHashMap通过分段锁和内部数据结构的优化,在多线程环境下提供了高效的并发访问。它支持高并发的插入、查找和删除操作,同时保证数据的一致性和正确性。但需要注意,在遍历ConcurrentHashMap时,由于Segements之间是独立的,可能会存在一些不一致的情况,因此在遍历时需谨慎。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值