🔍 開發者資源導航 🔍 |
---|
🏷? 博客主頁: 個人主頁 |
📚 專欄訂閱: JavaEE全棧專欄 |
多線程安全使用ArrayList
手動加鎖
日常中最常用的方法,使用synchronized進行加鎖,把代碼打包成一份,使其成為一個“原子”操作。
手動加鎖:可以只鎖必要的部分,減少競爭。適用于高并發場景。
使用Collections套殼
Collections.synchronizedList() 返回一個 線程安全的 List 包裝器,所有對 List 的操作(如 add(), remove(), get())都會自動加鎖,確保多線程安全。
List<Integer> arr = Collections.synchronizedList(new ArrayList<Integer>());
缺點:
- synchronizedList 只保證單個操作是原子的,但 遍歷(iteration)仍然需要手動加鎖。
- 鎖的粒度較大,導致:高并發寫操作時,線程會頻繁競爭同一把鎖,造成阻塞(contention)。讀操作也會被阻塞(即使讀操作本身不修改數據)。
適用于適用于低并發(少量寫操作)。
CopyOnWriteArrayList
CopyOnWriteArrayList在操作時不去加鎖,它在進行讀寫操作時使用了一種常見的思想方法:寫時拷貝。
他在修改時,先創建一個相同的數組,對這個新的數組進行修改操作,然后改變原數組的引用。
雖然復制過程不是原子的,但是由于提供了舊版本的數組,不影響其他線程讀取。
優點:
沒有加鎖~不會產生阻塞。
缺點:
- 如果數組很大,效率會非常大。
- 多個線程同時修改,也容易出現問題,可以會丟失修改數據。
- 只適用于特定場景,例如:服務器進行重新加載配置的時候。
多線程安全使用哈希表
Hashtable
Hashtable相比于Hashmap對各種public方法都加了synchronized,可以保證線程安全。
缺點:對整個哈希表進行了加鎖,在訪問時很容易造成競爭,造成效率降低。
ConcurrentHashMap
基于哈希表進行的多線程優化,他按照桶級別進行加鎖,而不是加一個全局鎖,有效降低鎖沖突的概率。
優化點一
在Hashtable中任意的兩個線程訪問不同的兩個元素都會造成競爭,但是實際上,處于兩個不同鏈表上的兩個元素是不會涉及線程安全問題的。
因此ConcurrentHashMap對每一個節點都分配一個鎖,可以減少修改不同變量時的鎖競爭。
那么這樣做會增加它占的空間嗎?在Java中每一個對象都可以作為鎖對象,因此我們完全可以讓每一個鏈表的頭結點當做鎖對象,因此并不會增加空間。
優化點二
上述優化點雖然可以減少鎖競爭,但是卻無法保證size()方法的線程安全,因為它的鎖不是全局鎖了,而是分別加鎖。因此ConcurrentHashMap的size改用了原子類進行存儲。
優化點三
ConcurrentHashMap針對擴容操作也進行了優化,在把舊哈希的元素搬運到新的哈希過程中,如果元素很多,耗時就會很長。
ConcurrentHashMap針對擴容的操作將其化整為零,既然一口氣搬運比較好使,那就一次只搬一部分,這樣的操作可以將擴容的耗時平均分擔開,而不會造成某次的操作時間過長。
感謝各位的觀看Thanks?(・ω・)ノ,如果覺得滿意的話留個關注再走吧。