CurrennthashMapソース解析

9509 ワード

何がconcurrenthashmapですか?
concurrenthashmap(略称chm)は、java 1.5が新しく導入したjava.util.co ncurrentパッケージのメンバーであり、hashtableの代わりとしています。なぜかというと、hashtableは全体の方法を同期させる構造を採用している。スレッドの安全を実現しましたが、性能が大幅に低下しました。hashmapです。同時進行の場合はミスしやすいです。したがって、安全を促進し、マルチスレッドで使用できるconcurrenthashmapです。
どのようにconcurrenthashmapを実現しますか?
まず構造方法を見てみましょう。これは一番下の構造方法です。
public ConcurrentHashMap(int initialCapacity,
                             float loadFactor, int concurrencyLevel) {
        if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
            throw new IllegalArgumentException();
        if (concurrencyLevel > MAX_SEGMENTS)
            concurrencyLevel = MAX_SEGMENTS;
        // Find power-of-two sizes best matching arguments
        int sshift = 0;
        int ssize = 1;
        while (ssize < concurrencyLevel) {
            ++sshift;//  ssize     
            ssize <<= 1;
        }
        this.segmentShift = 32 - sshift;//         ,     segment     
        this.segmentMask = ssize - 1;//segment    
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        int c = initialCapacity / ssize;
        if (c * ssize < initialCapacity)
            ++c;
        int cap = MIN_SEGMENT_TABLE_CAPACITY;
        while (cap < c)
            cap <<= 1;
        // create segments and segments[0]
        Segment s0 =
            new Segment(loadFactor, (int)(cap * loadFactor),
                             (HashEntry[])new HashEntry[cap]);
        Segment[] ss = (Segment[])new Segment[ssize];
        UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
        this.segments = ss;
    }
ここではhashmapと比較して分析したいのですが、彼らは似ているので、hashmapはentry[]であり、chmはsegmentsです。各segmentはhashmapであり、segmentに入るにはまだ対応する鍵が必要です。デフォルトのconcceurrenthasmapのsegment数は16です。各segment内のhashentry配列サイズも16です。threadshardは16*0.75です。
chmの位置付けはどうなりますか
   chm hash          
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);
    }
ここでkeyのhash値についてもう一回ハッシュしました。使う方法はwang/junnkinsのハッシュ・アルゴリズムで、ここでshはhash衝突を減らすためです。そうしないと、多くの数値が一つのsegmentにあります。このようにしないと、セグメントのロックの意味がなくなります。上記のコードはkeyの新しいhash値を算出しただけですが、このhash値でどうやって位置決めしますか?
  • 値を取得するには、まずどのsegmentを知ってからhashentryのindexを知る必要があります。最後のサイクルはindexの下の要素
         segment,:(h >>> segmentShift) & segmentMask。    h  4 ,segmentMask 15
         index:(tab.length - 1) & h  hashentry    1,      sement  h  
         :for (HashEntry e = (HashEntry) UNSAFE.getObjectVolatile
                               (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
                           e != null; e = e.next)
                           
           :if ((k = e.key) == key || (e.hash == h && key.equals(k)))
                                return e.value;
    
  • を巡回します。
    chm取得要素
    public V get(Object key) {
            Segment s; // manually integrate access methods to reduce overhead
            HashEntry[] tab;
            int h = hash(key);
            long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
            if ((s = (Segment)UNSAFE.getObjectVolatile(segments, u)) != null &&
                (tab = s.table) != null) {
                for (HashEntry e = (HashEntry) 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;
        }
    
    値を取得するには、まずどのsegmentを知ってからhashentryのindexを知って、最後のサイクルはindexの下の要素を遍歴する必要があります。
             segment,:(h >>> segmentShift) & segmentMask。    h  4 ,segmentMask 15
             index:(tab.length - 1) & h  hashentry    1,      sement  h  
             :for (HashEntry e = (HashEntry) UNSAFE.getObjectVolatile(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);e != null; e = e.next)
             :if ((k = e.key) == key || (e.hash == h && key.equals(k)))
                     return e.value;
    chm保存元素
      public V put(K key, V value) {
                Segment s;
                if (value == null)
                    throw new NullPointerException();
                int hash = hash(key);
                int j = (hash >>> segmentShift) & segmentMask;
                if ((s = (Segment)UNSAFE.getObject          // nonvolatile; recheck
                     (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
                    s = ensureSegment(j);
                return s.put(key, hash, value, false);
            }   
         jdk ,native           ,   openjdk  。 put                 
                 (threadshold)   ,   hashmap     ,          
            concurrenthashmap     ,                  
    concrrenthasmap容量判断
    public int size() {
            final Segment[] segments = this.segments;
            int size;
            boolean overflow; // true if size overflows 32 bits
            long sum;         // sum of modCounts
            long last = 0L;   // previous sum
            int retries = -1; // first iteration isn't retry
            try {
                for (;;) {
                    if (retries++ == RETRIES_BEFORE_LOCK) {
                        for (int j = 0; j < segments.length; ++j)
                            ensureSegment(j).lock(); // force creation
                    }
                    sum = 0L;
                    size = 0;
                    overflow = false;
                    for (int j = 0; j < segments.length; ++j) {
                        Segment seg = segmentAt(segments, j);
                        if (seg != null) {
                            sum += seg.modCount;
                            int c = seg.count;
                            if (c < 0 || (size += c) < 0)
                                overflow = true;
                        }
                    }
                    if (sum == last)
                        break;
                    last = sum;
                }
            } finally {
                if (retries > RETRIES_BEFORE_LOCK) {
                    for (int j = 0; j < segments.length; ++j)
                        segmentAt(segments, j).unlock();
                }
            }
            return overflow ? Integer.MAX_VALUE : size;
        }
    
             ,   concurrenthashmap    ,      ,                  ,  put,remove 
           ,         ,           ,    。           。         ,              
    chmが空かどうかの判断
    public boolean isEmpty() {
           
            long sum = 0L;
            final Segment[] segments = this.segments;
            for (int j = 0; j < segments.length; ++j) {
                Segment seg = segmentAt(segments, j);
                if (seg != null) {
                    if (seg.count != 0)
                        return false;
                    sum += seg.modCount;
                }
            }
            if (sum != 0L) { // recheck unless no modifications
                for (int j = 0; j < segments.length; ++j) {
                    Segment seg = segmentAt(segments, j);
                    if (seg != null) {
                        if (seg.count != 0)
                            return false;
                        sum -= seg.modCount;
                    }
                }
                if (sum != 0L)
                    return false;
            }
            return true;
        }
                   segment       ,       ,count      ,      
    modcount      ,    ,        。
    chm削除要素
    final V remove(Object key, int hash, Object value) {
                if (!tryLock())
                    scanAndLock(key, hash);
                V oldValue = null;
                try {
                    HashEntry[] tab = table;
                    int index = (tab.length - 1) & hash;
                    HashEntry e = entryAt(tab, index);
                    HashEntry pred = null;
                    while (e != null) {
                        K k;
                        HashEntry next = e.next;
                        if ((k = e.key) == key ||
                            (e.hash == hash && key.equals(k))) {
                            V v = e.value;
                            if (value == null || value == v || value.equals(v)) {
                                if (pred == null)
                                    setEntryAt(tab, index, next);
                                else
                                    pred.setNext(next);
                                ++modCount;
                                --count;
                                oldValue = v;
                            }
                            break;
                        }
                        pred = e;
                        e = next;
                    }
                } finally {
                    unlock();
                }
                return oldValue;
            }   
            
    思考
    1.hashmapのデフォルトサイズは1<<4、すなわち16ですが、concurrenthashmapは直接16.2.(k<