ConcurrentHashMap 從Java7 到 Java8的改變


一、關於分段鎖

1.分段鎖發展概況

集合框架很大程度減少了java程序員的重復勞動。在Java多線程環境中,以線程安全的方式使用集合類是一個首先考慮的問題。

能夠保證線程安全的哈希表中,ConcurrentHashMap是大家都熟知的,也知道它內部使用了分段鎖。然而,進入到Java8時代,分段鎖成為了歷史。

2.新版本ConcurrentHashMap

在Java8的ConcurrentHashMap中,分段鎖僅用來處理對象流。

Java7中,Segment繼承於ReentrantLock使用了顯示鎖,在Segment的實例方法中,每個更新操作內部又使用Unsafe來處理更新。這顯然是一種浪費。顯示鎖、Unsafe 這二者都是可以保證對對象的原子操作。使用一個即可。

Java7中的數據存儲在數組 final Segment<K,V>[] segments; 這個一個特定大小的Segment數組,

Segment繼承於ReentrantLock 故而可以在更新操作時使用顯示鎖。

二、Java7的ConcurrentHashMap

java7中,ConcurrentHashMap的內部類Segment.

Segment繼承了ReetrantLock,利用了顯示鎖,同時在更新操作中也使用了Unsafe.雙管齊下來保證線程安全。

static final class Segment<K,V> extends ReentrantLock implements Serializable {
        private static final long serialVersionUID = 2249069246763182397L;
        // tryLock()最多等待時間
        static final int MAX_SCAN_RETRIES =
            Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
        // 分段表,元素通過entryAt/setEntryAt這兩個方法提供了瞬時操作。
        transient volatile HashEntry<K,V>[] table;
        // 元素數量,通過鎖或可見性地瞬時讀取
        transient int count;
        // 修改次數
        transient int modCount;
		// 表大小超出threshold時,重新哈希運算。它的值 = (int)(capacity*loadFactor)
        transient int threshold;
		// 負載因子
        final float loadFactor;
        final V put(K key, int hash, V value, boolean onlyIfAbsent) {
            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;
        }

        /**
            移除:value為null時,只匹配key,否則,兩個都匹配
         */
        final V remove(Object key, int hash, Object value) {
            if (!tryLock())
                scanAndLock(key, hash);
            V oldValue = null;
            try {
                HashEntry<K,V>[] tab = table;
                int index = (tab.length - 1) & hash;
                HashEntry<K,V> e = entryAt(tab, index);
                HashEntry<K,V> pred = null;
                while (e != null) {
                    K k;
                    HashEntry<K,V> 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)
                              //  這里是cas操作
                                setEntryAt(tab, index, next);
                            else
                                pred.setNext(next);
                            ++modCount;
                            --count;
                            oldValue = v;
                        }
                        break;
                    }
                    pred = e;
                    e = next;
                }
            } finally {
                unlock();
            }
            return oldValue;
        }

        final void clear() {
            lock();
            try {
                HashEntry<K,V>[] tab = table;
                for (int i = 0; i < tab.length ; i++)
                // Unsafe 操作    
				setEntryAt(tab, i, null);
                ++modCount;
                count = 0;
            } finally {
                unlock();
            }
        }
    }                                                   

三、Java8的ConcurrentHashMap

Java8中,ConcurrentHashMap較之前版本有了很大的改變。

使用Node數組替代了Segment數組來存儲數據。Node數組中不再使用顯示鎖,而是Unsafe的樂觀鎖機制。

Segment予以保留,僅用來處理對象流的讀寫。

從如下Java8版本的ConcurrentHashMap$Segment源碼來看,分段鎖,基本棄用了。

    static class Segment<K,V> extends ReentrantLock implements Serializable {
        private static final long serialVersionUID = 2249069246763182397L;
        final float loadFactor;
        Segment(float lf) { this.loadFactor = lf; }
    }

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM