以下ConcurrentHashMap类是基于jdk1.7来分析。
JDK1.7中ConcurrentHashMap是通过segments数组和HashEntry数组+链表来进行实现的。利用锁分段技术,支持任务数量线程的读和一定数量线程的写。
我们看下ConcurrentHashMap是怎么进行put和get操作的。
1、ConcurrentHashMap的put方法不能插入null值(为什么?自行百度),在put kv值时,首先取key的hash值,通过hash值判断key所在的segment,然后使用unsafe类的本地方法获取此segments数组中hash值对应的segment是否为null(为什么用unsafe类呢?因为需要获取内存中最新的存储值,关于unsafe类直接操作内存,参考这里。),如果为null,则初始化segment元素,然后调用segment的put方法。ConcurrentHashMap类的put方法源码如下
//ConcurrentHashMap的put方法
public V put(K key, V value) {
Segment<K,V> s;
//value值为null,直接报异常
if (value == null)
throw new NullPointerException();
//两次hash,获得key的哈希值
int hash = hash(key);
//对hash值的高位和segmentMask掩码做按位与,确定key所在的segment(segmentMask=segment的长度-1)
int j = (hash >>> segmentShift) & segmentMask;
//通过Unsafe类获取segments数组中下标为j的元素,如果不存在就初始化segment。(SSHIFT和SBASE均为确定数组元素的内存位置,见以下变量声明和static块初始化)
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);
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long SBASE;
private static final int SSHIFT;
private static final long TBASE;
private static final int TSHIFT;
private static final long HASHSEED_OFFSET;
private static final long SEGSHIFT_OFFSET;
private static final long SEGMASK_OFFSET;
private static final long SEGMENTS_OFFSET;
static {
int ss, ts;
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class tc = HashEntry[].class;
Class sc = Segment[].class;
TBASE = UNSAFE.arrayBaseOffset(tc);
SBASE = UNSAFE.arrayBaseOffset(sc);
ts = UNSAFE.arrayIndexScale(tc);
ss = UNSAFE.arrayIndexScale(sc);
HASHSEED_OFFSET = UNSAFE.objectFieldOffset(
ConcurrentHashMap.class.getDeclaredField("hashSeed"));
SEGSHIFT_OFFSET