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