面試刷題10:ConcurrentHashMap如何保證線程安全?


image.png




集合框架中的HashTable,Stack,以及同步包裝集合在高並發場景下都非常低效,java提供了並發包應對高並發場景。


我是李福春,我在准備面試,今天的問題是?

java提供了哪些並發的容器?ConcurrentHashMap如何保證線程安全?

java體系中的並發容器



java體系中有如下同步容器:

1, HashTable,Stack 同步容器,內部使用sychronized關鍵字保證同步操作;

2,同步包裝器Collections.synchronizedMap(),內部也使用sychronized關鍵字保證同步操作;

3,並發包提供的同步容器,比如ConcurrentHashMap , CopyOnWriteArrayList , ArrayBlockingQueue,SynchronizedQueue

ConcurrentHashMap的同步分析



為何會出現ConcurrenHashMap?

1, HashTable在高並發場景下性能低下;

2,HashMap 不是線程安全的容器;

3,同步包裝器雖然使用同步方法快提升了部分性能,但是還是不適合高並發場景下的性能需求;



接下來回答問題,ConcurrentHashMap如何保證線程安全?

java7


java7版本使用的是分離鎖(segment)實際上是一種再入鎖(RetrantLock)來保證線程安全;
segment的數量是concurrentLevel決定,默認值是16;




擴容的時候是針對單個segment擴容的,寫操作也是,修改數據的時候鎖定的部分,所以比較高效;


但是后去size可能不太准確。

數據結構如下圖:

image.png

java8


java8中segment依然存在,不過不起結構上的作用,只起到保證序列化的兼容性。


內部使用volatile來保證數據存儲的可見性;


利用CAS操作,在特定場景下進行無鎖並發操作,內部的鎖實際用的是syncronized;


因為在jdk8中,syncronized已經得到性能的優化,並且對比再入鎖可以減少內存消耗。


見代碼:


final V putVal(K key, V value, boolean onlyIfAbsent) { if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (Node<K,V>[] tab = table;;) {
        Node<K,V> f; int n, i, fh; K fk; V fv;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            // 利用CAS去進行無鎖線程安全操作,如果bin是空的
            if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
                break; 
        }
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else if (onlyIfAbsent // 不加鎖,進行檢查
                 && fh == hash
                 && ((fk = f.key) == key || (fk != null && key.equals(fk)))
                 && (fv = f.val) != null)
            return fv;
        else {
            V oldVal = null;
            synchronized (f) {
                   // 細粒度的同步修改操作... 
                }
            }
            // Bin超過閾值,進行樹化
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}

小結


本節回答了java提供的並發容器分類,以及ConcurrentHashMap在java7,java8中的是如何保證線程安全的。


面試官喜歡問分離鎖,畫一下數據結構就明確了。

image.png

原創不易,轉載請注明出處。


免責聲明!

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



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