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]