ConcurrentHashMap(1.7)源码分析

说明:JDK1.7版本的ConcurrentHashMap

1. 底层结构&&构造方法
       采用了分段锁+重入锁的机制实现。首先分段,初始化的桶数量是16,如果并发级别设置为8,那么一个分段锁就管2个桶。每个分段锁也就是segment对象里面包含了一个小的HashEntry[ ] (相当于每个分段锁里面都有一个小的hashMap)。初始化的时候只初始化第一个segment,其他的segment都是null只有往里面put的时候才会初始化扩容,注意这里的扩容针对的是每个segment里面的小hashMap,也就说各自扩各自的。
       可能会出现这样的情况,一直往第一个segment里面put,那么第一个HashMap就会扩容,而其他的HashMap就不会扩容了。
ConcurrentHashMap(1.7)源码分析_第1张图片

//初始化方法
public ConcurrentHashMap(int initialCapacity,
                         float loadFactor, int concurrencyLevel) {
    if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
        throw new IllegalArgumentException();
    if (concurrencyLevel > MAX_SEGMENTS)
        concurrencyLevel = MAX_SEGMENTS;
    // Find power-of-two sizes best matching arguments
    int sshift = 0;
    int ssize = 1;
    while (ssize < concurrencyLevel) {
        ++sshift;
        ssize <<= 1;//调整到2的n次方
    }
    this.segmentShift = 32 - sshift;
    this.segmentMask = ssize - 1;//这里也侧面说明初始化的时候就将ssize固定了
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    int c = initialCapacity / ssize;
    if (c * ssize < initialCapacity)
        ++c;
    int cap = MIN_SEGMENT_TABLE_CAPACITY;
    while (cap < c)
        cap <<= 1;
    // create segments and segments[0]
    Segment<K,V> s0 =
        new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                         (HashEntry<K,V>[])new HashEntry[cap]);
    Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
    UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]//
    this.segments = ss;
}

2. put方法
先找到segment,然后看对应segment是否存在,不存在就先初始化一个segment,要是存在就往里面put。

public V put(K key, V value) {
    Segment<K,V> s;
    if (value == null)
        throw new NullPointerException();
    int hash = hash(key);
    //定位是哪个segment
    int j = (hash >>> segmentShift) & segmentMask;
    if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
         (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
        s = ensureSegment(j);
    return s.put(key, hash, value, false);
}


//保证并发安全的生成一个segment
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;
    //可能其他并发的线程已经创建了这个对象,那么就直接返回就好了,后面几个判断也是这个意思
    if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u)) == null) {
        Segment<K,V> proto = ss[0]; // use segment 0 as prototype
        int cap = proto.table.length;
        float lf = proto.loadFactor;
        int threshold = (int)(cap * lf);
        HashEntry<K,V>[] tab = (HashEntry<K,V>[])new HashEntry[cap];
        if ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
            == null) { // recheck
            Segment<K,V> s = new Segment<K,V>(lf, threshold, tab);
            //自旋+CAS,如果是空那么就走进去进行CAS,CAS过程中要是发现期望值变了那么就返回false,接着自旋。如果没变就CAS执行成功返回true那么就break。
            while ((seg = (Segment<K,V>)UNSAFE.getObjectVolatile(ss, u))
                   == null) {
                if (UNSAFE.compareAndSwapObject(ss, u, null, seg = s))
                    break;
            }
        }
    }
    return seg;
}

3. trylock和lock
ConcurrentHashMap(1.7)源码分析_第2张图片
ConcurrentHashMap(1.7)源码分析_第3张图片

你可能感兴趣的:(java基础)