同步類容器和併發類容器

2021-10-24 21:34:17 字數 2971 閱讀 2937

**

同步類容器都是執行緒安全的,同步容器類包括 vector 和 hashtable,二者都是早期 jdk 的一部分,此外還包括在 jdk1.2 當中新增的一些功能相似的類,這些同步的封裝類是由 collections.synchronized*** 等工廠方法建立的。 但在某些場景下可能需要加鎖來保護復合操作。復合類操作如:迭代(反覆訪問元素, 遍歷完容器中所有的元素)、跳轉(根據指定的順序找到當前元素的下乙個元素)、以及條件運算。這些復合操作在多執行緒併發地修改容器時,可能會表現出意外的行為,最經典的便是 concurrentmodificationexception,原因是當容器迭代的過程中,被併發的修改了內容,這是由於早期迭代器設計的時候並沒有考慮併發修改的問題,示例**如下:

修改很簡單,我們只需要把arraylist換為同步類容器就好。

通過閱讀原始碼發現,同步類容器其底層的機制無非就是用傳統的 synchronized 關鍵 字對每個公用的方法都進行同步,使得每次只能有乙個執行緒訪問容器的狀態。這很明顯不滿 足我們今天網際網路時代高併發的需求,在保證執行緒安全的同時,也必須要有足夠好的效能。

****

jdk 5.0 以後提供了多種併發類容器來替代同步類容器從而改善效能。同步類容器的狀態都是序列化的。他們雖然實現了執行緒安全,但是嚴重降低了併發性,在多執行緒環境時, 嚴重降低了應用程式的吞吐量。 併發類容器是專門針對併發設計的,使用 concurrenthashmap 來代替給予雜湊的傳統的 hashtable,而且在 concurrenthashmap 中,新增了一些常見復合操作的支援。以及使用了 copyonwritearraylist 代替 voctor,併發的 copyonwritearrayset,以及併發的 queue,concurrentlinkedqueue 和 linkedblockingqueue,前者是高效能的佇列,後者是以阻塞形式的佇列,具體實現 queue 還有很多,例如 arrayblockingqueue、 priorityblockingqueue、synchronousqueue 等。

****

由於 hashmap 是執行緒不同步的,雖然處理資料的效率高,但是在多執行緒的情況下存在著安全問題,因此設計了 currenthashmap 來解決多執行緒安全問題。

hashmap 在 put 的時候,插入的元素超過了容量(由負載因子決定)的範圍就會觸發擴容操作,就是 rehash,這個會重新將原陣列的內容重新 hash 到新的擴容陣列中,在多執行緒的環境下,存在同時其他的元素也在進行 put 操作,如果 hash 值相同,可能出現同時 在同一陣列下用鍊錶表示,造成閉環,導致在 get 時會出現死迴圈,所以 hashmap 是執行緒不安全的。

jdk7 下的 currenthashmap

在 jdk1.7 版本中,concurrenthashmap 的資料結構是由乙個 segment 陣列和多個 hashentry 組成,主要實現原理是實現了鎖分離的思路解決了多執行緒的安全問題,如下圖所示:

segment 陣列的意義就是將乙個大的 table 分割成多個小的 table 來進行加鎖,也就是上面的提到的鎖分離技術,而每乙個segment 元素儲存的是 hashentry 陣列+鍊錶,這個和 hashmap 的資料儲存結構一樣。 concurrenthashmap 內部使用段(segment)來表示這些不同的部分,每個段其實就是個小的hashtable,它們有自己的鎖。只要多個修改操作發生在不同的段上,它們就可以併發進行。把乙個整體分成了16個段(segment)。也就是最高支援 16 個執行緒的併發修改操作。這也是在多執行緒場景時減小鎖的粒度從而降低鎖競爭的一種方案。並且**中大多共享變數使用 volatile 關鍵字宣告,目的是第一時間獲取修改的內容,效能非常好。

jdk8 的 concurrenthashmap

jdk1.8 的實現已經摒棄了 segment 的概念,而是直接用 node 陣列+鍊錶+紅黑樹的數 據結構來實現,併發控制使用 synchronized 和 cas 來操作,整個看起來就像是優化過且

執行緒安全的 hashmap,雖然在 jdk1.8 中還能看到 segment 的資料結構,但是已經簡化了 屬性,只是為了相容舊版本。

node 是 concurrenthashmap 儲存結構的基本單元,繼承於hashmap中的 entry,用於儲存資料,node 資料結構很簡單,就是乙個鍊錶,但是只允許對資料進行查詢,不允許進行修改。

copyonwrite 容器

copy-on-writef 簡稱 cow,是一種用於程式設計中的優化策略。 jdk 裡的 cow 容器有兩種: copyonwritearraylist 和 copyonwritearrayset, cow 容器非常有用,可以在非常多的併發場景中使用到。

什麼是 copyonwrite 容器?

copyonwrite 容器即寫時複製的容器。通俗的理解是當我們往乙個容器新增元素的時 候,不直接往當前容器新增,而是先將當前容器進行 copy,複製出乙個新的容器,然後新的容器裡新增元素,新增完元素之後,再將原容器的引用指向新的容器。這樣做的好處是我 們可以對 copyonwrite 容器進行併發的讀,而不需要加鎖,因為當前容器不會新增任何元素。所以 copyonwrite 容器也是一種讀寫分離的思想,讀和寫不同的容器。

同步類容器和併發類容器

同步類容器都是執行緒安全的,但是某些場景下可能需要加鎖來保護復合操作。復合類操作如 迭代 反覆訪問元素,遍歷完容器中所有的元素 跳轉 根據指定的順序找到當前元素的下乙個元素 以及條件運算。這些復合操作在多執行緒併發地修改容器時,可能會表現出意外的行為,最經典的便是concurrentmodifica...

同步類容器和併發類容器

同步類容器都是執行緒安全的,但在某些場景下可能需要加鎖來保護復合操作。復合類操作。如 迭代 反覆訪問元素,遍歷完容器中所有的元素 跳轉 根據指定的順序找到當前元素的下乙個元素 以及條件運算。這些復合操作在多執行緒併發地修改容器時,可能會表現出意外的行為,最經典的便是concurrentmodific...

同步類容器和併發類容器

一 同步類容器 同步類容器都是執行緒安全的,但在某些場景下可能需要加鎖來保護復合操作。復合類操作如 迭代 反覆訪問元素,遍歷完容器中的所有元素 跳轉 根據指定的順序找到當前元素的下乙個元素 以及條件運算。這些復合操作在多執行緒併發的修改容器時,可能會表現出意外的行為,最經典的便是concurrent...