【Explore SRC】查阅ConcurrentHashMap笔记

看ConcurrentHashMap的,一般都看过HashMap的了,那么我们就直奔主题吧。

> 参考的优秀文章

Java集合---ConcurrentHashMap原理分析

> 构造方法

/**
 * The default concurrency level for this table, used when not
 * otherwise specified in a constructor.
 */
static final int DEFAULT_CONCURRENCY_LEVEL = 16;

......

public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel)

直接来到这个构造方法,可以看到,默认的concurrencyLevel为16,可以理解为分16个segment,操作时以segment为粒度大小加锁。

在HashMap基础上增加了:

1、计算segmentShift、segmentMask(用途下面会提到)

2、计算cap,实例化ss(segment的数组)、s0(第一个segment)(用途下面会提到)

 

int sshift = 0;
int ssize = 1;
while (ssize < concurrencyLevel) {
    ++sshift;
    ssize <<= 1;
}
this.segmentShift = 32 - sshift;
this.segmentMask = ssize - 1;

这段代码为了计算segmentShift(如需加入某元素,将它分配到某个segment需移动的位数)、segmentMask(此掩码用于计算分配到哪个segment)

concurrencyLevel可以看为分多少个segment,即segment[]的长度

segmentMask这个掩码后面被用来跟hash值的高位做&操作,从而确定分配到哪个segment;

sshift为segmentMask二进制的位数,用来求segmentShift;

而segmentShift后面用来为hash值右移的位数,从而获取hash的高多少位来做&运算。

 

这段代码简单打印各个值,便于理解:

public class SegmentShiftMask {

    public static void main(String[] args) {
        for (int i = 1; i < 20; i++) {
            test(i);
        }
    }
    
    public static void test(int concurrencyLevel) {
        int sshift = 0;
        int ssize = 1;
        while (ssize < concurrencyLevel) {
            ++sshift;
            ssize <<= 1;
        }
        int segmentShift = 32 - sshift;
        int segmentMask = ssize - 1;
        
        System.out.println("concurrencyLevel : " + concurrencyLevel + " \t= " + String.format("%08d", Integer.valueOf(Integer.toBinaryString(concurrencyLevel))));
        System.out.println("sshift           : " + sshift);
        System.out.println("segmentShift     : " + segmentShift);
        System.out.println("segmentMask      : " + segmentMask + " \t= " + String.format("%08d", Integer.valueOf(Integer.toBinaryString(segmentMask))));
        System.out.println("--------");
    }

}
View Code

打印结果:

concurrencyLevel : 15     = 00001111
sshift           : 4
segmentShift     : 28
segmentMask      : 15     = 00001111
--------
concurrencyLevel : 16     = 00010000
sshift           : 4
segmentShift     : 28
segmentMask      : 15     = 00001111
--------
concurrencyLevel : 17     = 00010001
sshift           : 5
segmentShift     : 27
segmentMask      : 31     = 00011111
--------
concurrencyLevel : 18     = 00010010
sshift           : 5
segmentShift     : 27
segmentMask      : 31     = 00011111
--------
concurrencyLevel : 19     = 00010011
sshift           : 5
segmentShift     : 27
segmentMask      : 31     = 00011111
--------
View Code

 

int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
    ++c;
int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
    cap <<= 1;

这段是求cap(segment中包含的table数组的大小),这个cap的大小是2的N次方。

 

int hash = hash(key);
int j = (hash >>> segmentShift) & segmentMask;

计算key的哈希值,右移segmentShift取哈希值的高位,再与掩码做&运算,得到j就是该把插入的元素放在segment[]的哪个位置了。

 

if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
     (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
    s = ensureSegment(j);

获取segment。

 

return s.put(key, hash, value, false);

......

static final class Segment<K,V> extends ReentrantLock implements Serializable {

......

    final V put(K key, int hash, V value, boolean onlyIfAbsent) {
        HashEntry<K,V> node = tryLock() ? null :
            scanAndLockForPut(key, hash, value);
        V oldValue;
            try {
        ......
        
        } finally {
            unlock();
        }
        return oldValue;
    }
    
......

}

Segment以ReentrantLock方式加锁,保证线程安全,且加锁的范围在Segment,不像HashTable整体加锁了。

scanAndLockForPut()中tryLock()尝试非阻塞方式获取锁并计算尝试次数,到一定次数后在使用中lock()阻塞方式获取锁。

 

HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);
for (HashEntry<K,V> e = first;;) {
    if (e != null) {
        K k;
        if ((k = e.key) == key ||
            (e.hash == hash && key.equals(k))) {
            oldValue = e.value;
            if (!onlyIfAbsent) {
                e.value = value;
                ++modCount;
            }
            break;
        }
        e = e.next;
    }
    else {
        if (node != null)
            node.setNext(first);
        else
            node = new HashEntry<K,V>(hash, key, value, first);
        int c = count + 1;
        if (c > threshold && tab.length < MAXIMUM_CAPACITY)
            rehash(node);
        else
            setEntryAt(tab, index, node);
        ++modCount;
        count = c;
        oldValue = null;
        break;
    }
}

......

static final class HashEntry<K,V> {
        final int hash;
        final K key;
        volatile V value;
        volatile HashEntry<K,V> next;
......

hash与tab.length做&操作,获取该放在哪个下标指向。覆盖数据、新增数据的逻辑与HashMap类似。

 

> 附

本文并未完整地描述ConcurrentHashMap的整体概括,只记录笔者浏览的部分笔记。

 

部分图纸笔记:

 

 

 

posted @ 2016-03-15 21:45  nick_huang  阅读(379)  评论(0编辑  收藏  举报