//concurrentHashmap維護一個segment數組,將元素分成若干段(第一次hash)
//segments的每一個segment維護一個鏈表數組
/**
* The segments, each of which is a specialized hash table.
*/
final Segment<K,V>[] segments;
//與hashmap不同 ReentrantLock
static final class Segment<K,V> extends ReentrantLock implements Serializable{}
//與hashmap不同 volatile
transient volatile HashEntry<K,V>[] table;
private transient final int hashSeed = randomHashSeed(this);
static final int DEFAULT_INITIAL_CAPACITY = 16;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* The default concurrency level for this table, used when not
* otherwise specified in a constructor.
*/
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
/*
與hashmap類似,concurrentHashmap也採用了鏈表作爲每個hash桶中的元素,不過concurrentHashmap又有些不同.
HashEntry的key,hash採用final,可以避免併發修改問題,HashEntry鏈的尾部是不能修改的,而next和value採用volatile,
可以避免使用同步造成的併發性能災難,新版(jdk1.7)的concurrentHashmap大量使用java Unsafe類提供的原子操作,
直接調用底層操作系統,提高性能(這塊我也不是特別清楚)
*/
/**
* ConcurrentHashMap list entry. Note that this is never exported
* out as a user-visible Map.Entry.
*/
static final class HashEntry<K,V> {
final int hash;
final K key;
//與hashmap不同 volatile
volatile V value;
volatile HashEntry<K,V> next;
HashEntry(int hash, K key, V value, HashEntry<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
/*
1.6的jdk採用了樂觀鎖的方式處理了get方法,在get的時候put方法正在new對象,而此時value並未賦值,這時判斷爲空則加鎖訪問.
1.7並沒有判斷value=null的情況,不知爲何
跟同事溝通過,無論是1.6還是1.7的實現,實際上都是一種樂觀的方式,而樂觀的方式帶來的是性能上的提升,但同時也帶來數據的弱一致性,
如果你的業務是強一致性的業務,可能就要考慮另外的解決辦法(用Collections包裝或者像jdk6中一樣二次加鎖獲取)
http://ifeve.com/concurrenthashmap-weakly-consistent/
這篇文章可以很好地解釋弱一致性問題
*/
/*
get方法並沒有調用鎖的痕跡,那麼是如何實現線程安全的呢?這就得益於volatile關鍵字,保證了共享的東西的可見性
*/
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code key.equals(k)},
* then this method returns {@code v}; otherwise it returns
* {@code null}. (There can be at most one such mapping.)
*
* @throws NullPointerException if the specified key is null
*/
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;
// 與hashmap不同 UNSAFE.getObjectVolatile
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 V put(K key, V value) {
Segment<K,V> s;
if (value == null)
throw new NullPointerException();
int hash = hash(key);
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);
}
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
// ReentrantLock.tryLock() 獨佔鎖
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);
for (HashEntry<K,V> e = first;;) {
if (e != null) {
K k;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
oldValue = e.value;
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
break;
}
e = e.next;
}
else {
if (node != null)
node.setNext(first);
else
node = new HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();
}
return oldValue;
}
ConcurrentHashMap 1.7
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.