學做網(wǎng)站視頻論壇免費友情鏈接網(wǎng)
📕 引言
我們之前講過的集合類,,大部分都不是線程安全的.
Vector,?Stack, HashTable, 是線程安全的(都是自帶了synchronized,不建議用), 其他的集合類不是線程安全的。
注意:加鎖不能保證線程一定安全,不加鎖也不能確定線程一定不安全,具體代碼具體分析
為什么不建議使用呢?
因為我們在使用的時候,這些類就會自動的加鎖,雖然編譯器會自動優(yōu)化為沒有鎖競爭的線程進行鎖消除的優(yōu)化,但是呢萬一編譯器沒有優(yōu)化好,就麻煩了
如果需要用到其他的類,就需要手動加鎖,來保證線程安全(不同情況加鎖方式也不同)
🌲多線程環(huán)境使用 ArrayList
🚩自己使用同步機制 (synchronized 或者 ReentrantLock)
這里就不在過多講述了
🚩Collections.synchronizedList(new ArrayList);
-
synchronizedList 是標準庫提供的一個基于 synchronized 進行線程同步的List.
-
synchronizedList 的關鍵操作上都帶有 synchronized
相當于給ArrayList這些集合類,套一層殼,殼上是給關鍵方法都加了synchronized
🚩使用 CopyOnWriteArrayList
也是一種解決線程安全的問題的做法
使用CopyOnWrite容器即"寫時拷貝"的容器
-
當我們往一個容器添加元素的時候,不直接往當前容器添加,而是先將當前容器進行Copy,復制出一個新的容器,然后新的容器里添加元素,
-
添加完元素之后,再將原容器的引用指向新的容器
例子:
這樣做的好處是我們可以對CopyOnWrite容器進行并發(fā)的讀,而不需要加鎖,因為當前容器不會添加任何元素。
所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器。
優(yōu)點:
- 在讀多寫少的場景下, 性能很高, 不需要加鎖競爭.
缺點:
-
占用內存較多.
-
新寫的數(shù)據(jù)不能被第一時間讀取到
🎍多線程環(huán)境使用隊列
這里簡單討論一下即可,前面討論過。
-
ArrayBlockingQueue
基于數(shù)組實現(xiàn)的阻塞隊列 -
LinkedBlockingQueue
基于鏈表實現(xiàn)的阻塞隊列 -
PriorityBlockingQueue
基于堆實現(xiàn)的帶優(yōu)先級的阻塞隊列 -
TransferQueue
最多只包含一個元素的阻塞隊列
🎋多線程環(huán)境使用哈希表
HashMap 本身不是線程安全的.
在多線程環(huán)境下使用哈希表可以使用:
-
Hashtable(自帶鎖,只是線程比較安全,但不推薦使用)
-
ConcurrentHashMap(多線程使用哈希表,優(yōu)先考慮這個)
🚩Hashtable
Hashtable在實現(xiàn)過程中只是簡單的把關鍵方法加上了 synchronized 關鍵字.
觀察源碼即可:(就不一一展示了)
這相當于直接針對 Hashtable 對象本身加鎖.
-
如果多線程訪問同一個 Hashtable 就會直接造成鎖沖突.
-
size 屬性也是通過 synchronized 來控制同步, 也是比較慢的.
-
一旦觸發(fā)擴容, 就由該線程完成整個擴容過程. 這個過程會涉及到大量的元素拷貝, 效率會非常低
🚩ConcurrentHashMap
相比于 Hashtable 做出了一系列的改進和優(yōu)化.?
1.使用"鎖桶"的方式,來代替"一把全局鎖",有效的降低鎖沖突的概率
2.HashMap的size,即使你插入的的元素是不同的鏈表上的元素,也會涉及到多個線程針對同一size變量進行修改。
引入 CAS ,通過 CAS 的方式來修改size,也就避免了加鎖操作。也可進一步的提升效率
3.針對擴容操作的優(yōu)化
核心思路:"化整為零"
注意:
ConcurrentHashMap 是一個工作中非常常用,面試非常高頻的問題!!!整個多線程進行部分,哪怕別的不記得,這個一定記得。
細節(jié):
🌳相關面試題
1.ConcurrentHashMap的讀是否要加鎖,為什么?
讀操作沒有加鎖. 目的是為了進一步降低鎖沖突的概率. 為了保證讀到剛修改的數(shù)據(jù), 搭配了 volatile 關鍵字.
2.介紹下 ConcurrentHashMap的鎖分段技術?
這個是 Java1.7 中采取的技術. Java1.8 中已經(jīng)不再使用了. 簡單的說就是把若干個哈希桶分成一個 “段” (Segment),針對每個段分別加鎖. 目的也是為了降低鎖競爭的概率. 當兩個線程訪問的數(shù)據(jù)恰好在同一個段上的時候, 才觸發(fā)鎖競爭.
3.ConcurrentHashMap在jdk1.8做了哪些優(yōu)化?
取消了分段鎖, 直接給每個哈希桶(每個鏈表)分配了一個鎖(就是以每個鏈表的頭結點對象作為鎖對 象). 將原來 數(shù)組 + 鏈表的實現(xiàn)方式改進成 數(shù)組 + 鏈表 / 紅黑樹 的方式. 當鏈表較長的時候(大于等于 8 個元素)就轉換成紅黑樹.
4.Hashtable和HashMap、ConcurrentHashMap 之間的區(qū)別?
- HashMap: 線程不安全. key 允許為 null
- Hashtable: 線程安全. 使用 synchronized 鎖,Hashtable 對象, 效率較低. key 不允許為 null.
- ConcurrentHashMap: 線程安全. 使用synchronized 鎖每個鏈表頭結點, 鎖沖突概率低, 充分利用 CAS 機制. 優(yōu)化了擴容方式. key 不允許為 null