HashTable和Collections的內部類SynchronizedMap裏的同步,都是用synchronized來實現的,每次都是鎖整個表,因此同一時刻只能有一個線程操作hash表。
而ConcurrentHashMap中使用了Segment[]來存儲數據,Segment繼承自ReentrantLock類(這樣執行lock操作就方便了),默認情況下有16個Segment,當put數據時,會看這個key的hash值對應到哪個Segment裏面,然後只鎖這個Segement,所以它允許多個線程同時put。(讀操作一般不需要加鎖,除非發生特殊情況)
Segemnt
/**
* The segments, each of which is a specialized hash table
*/
final Segment<K,V>[] segments;
/**
* Segments are specialized versions of hash tables. This
* subclasses from ReentrantLock opportunistically, just to
* simplify some locking and avoid separate construction.
*/
static final class Segment<K,V> extends ReentrantLock implements Serializable {
ConcurrentHashMap的put()方法:
public V put(K key, V value) {
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
return segmentFor(hash).put(key, hash, value, false);
}
/**
* Returns the segment that should be used for key with given hash
* @param hash the hash code for the key
* @return the segment
*/
final Segment<K,V> segmentFor(int hash) {
return segments[(hash >>> segmentShift) & segmentMask];
}
先根據key的hash值計算所屬的Segment,然後執行該Segment的put()方法。
Segemnt的put()方法:
V put(K key, int hash, V value, boolean onlyIfAbsent) {
lock();
try {
int c = count;
if (c++ > threshold) // ensure capacity
rehash();
HashEntry<K,V>[] tab = table;
int index = hash & (tab.length - 1);
HashEntry<K,V> first = tab[index];
HashEntry<K,V> e = first;
while (e != null && (e.hash != hash || !key.equals(e.key)))
e = e.next;
V oldValue;
if (e != null) {
oldValue = e.value;
if (!onlyIfAbsent)
e.value = value;
}
else {
oldValue = null;
++modCount;
tab[index] = new HashEntry<K,V>(key, hash, first, value);
count = c; // write-volatile
}
return oldValue;
} finally {
unlock();
}
}
由於Segment繼承自ReentrantLock,因此可以很方便的調用lock()和unlock().
綜述,由於ConcurrentHashMap採用了分段鎖(或者說鎖分離),並且對存儲size等操作做了一些加鎖優化,因此其效率要高一些。重點還是分段鎖。
有一篇介紹ConcurrentHashMap的文章,分享一下:
[url]http://www.iteye.com/topic/344876[/url]