仁夹篇 发表于 4 小时前

21. Java JUC源码分析系列笔记-JDK1.7的ConcurrentHashMap

目录

[*]1. 构造方法
[*]2. put方法

[*]2.1. hash
[*]2.2. ensureSegment
[*]2.3. segment的put方法

[*]2.3.1. scanAndLockForPut
[*]2.3.2. rehash


[*]3. get
[*]4. containsKey方法
[*]5. remove

[*]5.1. segmentForHash
[*]5.2. segment的remove

[*]总结
[*]6. 参考

1. 构造方法

public ConcurrentHashMap() {
        //16,0.75f,16
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}2. put方法

public V put(K key, V value) {
    Segment<K,V> s;
    //value不能为空
    if (value == null)
      throw new NullPointerException();
    //计算key的hash值
    int hash = hash(key);
    //在用hash值计算j
    int j = (hash >>> segmentShift) & segmentMask;
    if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
         //用j计算地址,并且cas获取segment对象
         (segments, (j << SSHIFT) + SBASE)) == null) //in ensureSegment
      //初始化
      s = ensureSegment(j);
    //调用segment的put方法
    return s.put(key, hash, value, false);
}2.2. ensureSegment

private int hash(Object k) {
    int h = hashSeed;

    if ((0 != h) && (k instanceof String)) {
      return sun.misc.Hashing.stringHash32((String) k);
    }

    h ^= k.hashCode();

    // Spread bits to regularize both segment and index locations,
    // using variant of single-word Wang/Jenkins hash.
    h += (h <<15) ^ 0xffffcd7d;
    h ^= (h >>> 10);
    h += (h <<   3);
    h ^= (h >>>6);
    h += (h <<   2) + (h << 14);
    return h ^ (h >>> 16);
}2.3.1. scanAndLockForPut

private Segment<K,V> ensureSegment(int k) {
    final Segment<K,V>[] ss = this.segments;
    long u = (k << SSHIFT) + SBASE; // raw offset
    Segment<K,V> seg;
    //segment为空,需要初始化。为什么使用cas而不是直接用下标???
    if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {
            //原型模式,使用第一个segment作为原型
      Segment<K,V> proto = ss; // use segment 0 as prototype
      //获取第一个segment的capacity,loadFactor,threshold,(后续用于创建entry table和segment)
      int cap = proto.table.length;
      float lf = proto.loadFactor;
      int threshold = (int)(cap * lf);
      
      //创建entry table(后续用于创建segment)
      HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry;
      //重新确认为空,不为空说明已经有其他线程初始化了,直接退出,避免进入死循环+cas浪费cpu
      if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
            == null) { // recheck
            //创建segment
            Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);
            //死循环+cas设置segment
            while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
                   == null) {
                if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))
                  break;
            }
      }
    }
    return seg;
}2.3.2. rehash

private void rehash(HashEntry node) {      /*         * Reclassify nodes in each list to new table.Because we         * are using power-of-two expansion, the elements from         * each bin must either stay at same index, or move with a         * power of two offset. We eliminate unnecessary node         * creation by catching cases where old nodes can be         * reused because their next fields won't change.         * Statistically, at the default threshold, only about         * one-sixth of them need cloning when a table         * doubles. The nodes they replace will be garbage         * collectable as soon as they are no longer referenced by         * any reader thread that may be in the midst of         * concurrently traversing table. Entry accesses use plain         * array indexing because they are followed by volatile         * table write.         */      HashEntry[] oldTable = table;      int oldCapacity = oldTable.length;      //新的容量为旧容量*2      int newCapacity = oldCapacity >> segmentShift) & segmentMask) > segmentShift) & segmentMask) > segmentShift) & segmentMask)
页: [1]
查看完整版本: 21. Java JUC源码分析系列笔记-JDK1.7的ConcurrentHashMap