ConcurrentHashMap 1.7

//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;
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章