併發集合和普通集合如何區別?


併發集合和普通集合如何區別?
併發集合常見的有 ConcurrentHashMap、 ConcurrentLinkedQueue、 ConcurrentLinkedDeque 等。併發集合位 於 java.util.concurrent 包 下 , 是 jdk1.5 之 後 才 有 的
在 java 中有普通集合、同步(線程安全)的集合、併發集合。普通集合一般性能最高,可是不保證多線程的安全性和併發的可靠性。線程安全集合僅僅是給集合添加了 synchronized 同步鎖,嚴重犧牲了性能,並且對併發的效率就更低了,併發集合則經過複雜的策略不只保證了多線程的安全又提升的併發時的效率。java

參考閱讀:
ConcurrentHashMap 是線程安全的 HashMap 的實現,默認構造一樣有 initialCapacity 和 loadFactor 屬性,不過還多了一個 concurrencyLevel 屬性,理解爲併發量,三屬性默認值分別爲 1六、 0.75 及 16。其內部使用鎖分段技術,維持這鎖Segment 的數組,在 Segment 數組中又存放着 Entity[]數組,內部 hash 算法將數據較均勻分佈在不一樣鎖中。
put 操做:並無在此方法上加上 synchronized,首先對 key.hashcode 進行 hash 操做,獲得 key 的 hash 值。
hash 操做的算法和 map 也不一樣,根據此 hash 值計算並獲取其對應的數組中的 Segment 對象(繼承自 ReentrantLock),接着調用此 Segment 對象的 put 方法來完成當前操做。
ConcurrentHashMap 基於 concurrencyLevel 劃分出了多個 Segment 來對 key-value 進行存儲,從而避免每次 put 操做都得鎖住整個數組。在默認的狀況下,最佳狀況下可容許 16 個線程併發無阻塞的操做集合對象,儘量地減小併發時的阻塞現象。
get(key)
首先對 key.hashCode 進行 hash 操做,基於其值找到對應的 Segment 對象,調用其 get 方法完成當前操做。而 Segment 的 get 操做首先經過 hash 值和對象數組大小減 1 的值進行按位與操做來獲取數組上對應位置的HashEntry。
在這個步驟中,可能會由於對象數組大小的改變,以及數組上對應位置的 HashEntry 產生不一致性,那麼 ConcurrentHashMap 是如何保證的?
對象數組大小的改變只有在 put 操做時有可能發生,因爲 HashEntry 對象數組對應的變量是 volatile 類型的,所以能夠保證如 HashEntry 對象數組大小發生改變,讀操做可看到最新的對象數組大小。
在獲取到了 HashEntry 對象後,怎麼能保證它及其 next 屬性構成的鏈表上的對象不會改變呢?這點
ConcurrentHashMap 採用了一個簡單的方式,即 HashEntry 對象中的 hash、 key、 next 屬性都是 final 的,這也就意味着沒辦法插入一個 HashEntry 對象到基於 next 屬性構成的鏈表中間或末尾。這樣就能夠保證當獲取到 HashEntry對象後,其基於 next 屬性構建的鏈表是不會發生變化的。
ConcurrentHashMap 默認狀況下采用將數據分爲 16 個段進行存儲,而且 16 個段分別持有各自不一樣的鎖Segment,鎖僅用於 put 和 remove 等改變集合對象的操做,基於 volatile 及 HashEntry 鏈表的不變性實現了讀取的不加鎖。這些方式使得 ConcurrentHashMap 可以保持極好的併發支持,尤爲是對於讀遠比插入和刪除頻繁的 Map而言,而它採用的這些方法也可謂是對於 Java 內存模型、併發機制深入掌握的體現。算法

相關文章
相關標籤/搜索