一、早期線程安全的集合
我們先從早期的線程安全的集合說起,它們是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裏的包裝類就能辦到