自从jdk1.5之后,JDK增加了ConcurrentMap接口,这个接口是对应的 java.util.Map并发集合,提供了putIfAbsent, remove,replace三个接口方法,concurrent包也提供了ConcurrentMap的实现类ConcurrentHashMap:
首先说一下ConcurrentHashMap的实现方式:
相比线程安全的HashTable各种方法都加了同步方法,ConcurrentHashMap进行了相应的改进。首先将整个table分成了多个Segment,默认为16个
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
//...
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;
接下来通过源码可以看到Segment存储的方式仍然为HashEntry,
Segment(float lf, int threshold, HashEntry<K,V>[] tab) {
//...
}
内部操作也都是使用的tryLock,
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;
}
final V remove(Object key, int hash, Object value) {
if (!tryLock())
scanAndLock(key, hash);
V oldValue = null;
try {
//...
} finally {
unlock();
}
return oldValue;
}
这样看来这个Segment相当于小的HashTable,也就是说ConcurrentHashMap将HashTable在操作上锁定由全表锁定变成了部分锁定的方式增加了并发访问。但是有别于HashTable所有操作都是使用了同步synchronize,ConcurrentHashMap在get,clear,values方法中并没有使用同步方法,这里也就体现了ConcurrentHashMap的弱一致性。
public V get(Object key) {
Segment<K,V> s; // manually integrate access methods to reduce overhead
HashEntry<K,V>[] tab;
int h = hash(key);
long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
if ((s = (Segment<K,V>)UNSAFE.getObjectVolatile(segments, u)) != null &&
(tab = s.table) != null) {
for (HashEntry<K,V> e = (HashEntry<K,V>) UNSAFE.getObjectVolatile
(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
e != null; e = e.next) {
K k;
if ((k = e.key) == key || (e.hash == h && key.equals(k)))
return e.value;
}
}
return null;
}
public void clear() {
final Segment<K,V>[] segments = this.segments;
for (int j = 0; j < segments.length; ++j) {
Segment<K,V> s = segmentAt(segments, j);
if (s != null)
s.clear();
//虽然Segment是使用的lock,但是当一个Segment被clear后,
//另一个正在clear时,原先被clear的有可能又被添加了数据
}
}
public Collection<V> values() {
Collection<V> vs = values;
return (vs != null) ? vs : (values = new Values());
}