Java執行緒安全的集合詳解
一、早期執行緒安全的集合
我們先從早期的執行緒安全的集合說起,它們是Vector和HashTable
1.Vector
Vector和ArrayList類似,是長度可變的陣列,與ArrayList不同的是,Vector是執行緒安全的,它給幾乎所有的public方法都加上了synchronized關鍵字。由於加鎖導致效能降低,在不需要併發訪問同一物件時,這種強制性的同步機制就顯得多餘,所以現在Vector已被棄用
2.HashTable
HashTable和HashMap類似,不同點是HashTable是執行緒安全的,它給幾乎所有public方法都加上了synchronized關鍵字,還有一個不同點是HashTable的K,V都不能是null,但HashMap可以,它現在也因為效能原因被棄用了
二、Collections包裝方法
Vector和HashTable被棄用後,它們被ArrayList和HashMap代替,但它們不是執行緒安全的,所以Collections工具類中提供了相應的包裝方法把它們包裝成執行緒安全的集合
List<E> synArrayList = Collections.synchronizedList(new ArrayList<E>());
Set<E> synHashSet = Collections.synchronizedSet(new HashSet<E>());
Map<K,V> synHashMap = Collections.synchronizedMap(new HashMap<K,V>());
...
Collections針對每種集合都聲明瞭一個執行緒安全的包裝類,在原集合的基礎上添加了鎖物件,集合中的每個方法都通過這個鎖物件實現同步
三、java.util.concurrent包中的集合
1.ConcurrentHashMap
ConcurrentHashMap和HashTable都是執行緒安全的集合,它們的不同主要是加鎖粒度上的不同。HashTable的加鎖方法是給每個方法加上synchronized關鍵字,這樣鎖住的是整個Table物件。而ConcurrentHashMap是更細粒度的加鎖
在JDK1.8之前,ConcurrentHashMap加的是分段鎖,也就是Segment鎖,每個Segment含有整個table的一部分,這樣不同分段之間的併發操作就互不影響
JDK1.8對此做了進一步的改進,它取消了Segment欄位,直接在table元素上加鎖,實現對每一行進行加鎖,進一步減小了併發衝突的概率
2.CopyOnWriteArrayList和CopyOnWriteArraySet
它們是加了寫鎖的ArrayList和ArraySet,鎖住的是整個物件,但讀操作可以併發執行
3.
除此之外還有ConcurrentSkipListMap、ConcurrentSkipListSet、ConcurrentLinkedQueue、ConcurrentLinkedDeque等,至於為什麼沒有ConcurrentArrayList,原因是無法設計一個通用的而且可以規避ArrayList的併發瓶頸的執行緒安全的集合類,只能鎖住整個list,這用Collections裡的包裝類就能辦到