經常使用的一些容器例如 ArrayList、HashMap、都不是線程安全的,最簡單的將這些容器變爲線程安全的方式,是給這些容器全部的方法都加上 synchronized 關鍵字。java
Java 的 Collections 中實現了這些同步容器:數組
簡單的使用以下:安全
List<String> list = Collections.synchronizedList(new ArrayList<>()); Map<Integer, String> map = Collections.synchronizedMap(new HashMap<>()); Set<String> set = Collections.synchronizedSet(new HashSet<>());
同步容器雖然簡單,可是相應的效率較低,由於鎖的粒度較大。數據結構
循環遍歷同步容器併發
若是在遍歷同步容器的時候,組合了多個方法,這會可能會存在競態條件,仍然不是線程安全的。解決的辦法即是對容器加鎖。例以下面這樣:性能
public static void main(String[] args) { List<String> list = Collections.synchronizedList(new ArrayList<>()); //省略添加數據的操做 String[] str = new String[list.size()]; int k = 0; synchronized (list){ Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ str[k ++] = iterator.next(); } } }
Java 中還提供了一系列併發容器,相比於同步容器,其性能更好。併發容器共分爲了四類:List、Map、Set、Queue。spa
List 中一個最主要的實現類是 CopyOnWriteArrayList
,CopyOnWrite,即寫時複製,這樣的好處是讀操做是無鎖的。線程
其實現原理是內部維護了一個數組,內部變量 array 指向了這個數組。須要寫時,並非在原數組上操做,而是將數組複製一份,在拷貝的數組中進行寫。完成後,將 array 指向新的數組。這樣一來,讀寫之間不互斥,效率獲得了很大的提高。3d
須要注意的是 CopyOnWriteArrayList 適用於讀多寫少的場景,而且須要接受讀寫的暫時不一致,由於在寫的時候,並行的讀操做可能並不能立刻看到寫的結果。code
Map 的兩個主要實現類是 ConcurrentHashMap
和 ConcurrentSkipListMap
,二者主要的區別是:前者是無序的,後者是有序的。
在 Java 1.7 中,ConcurrentHashMap 的實現使用的是分段鎖技術,其內部主要的數據結構是 Segment 和 HashEntry,ConcurrentHashMap 包含了一個 Segment 數組,每一個 Segment 又包含一個 HashEntry 數組,每一個 HashEntry 是一個存儲數據的鏈表結構。
其中 Segment 繼承了 ReentrantLock,每一個 Segment 都有對應的鎖,須要修改數據的時候,須要獲取這把鎖。修改不一樣的 Segment 數據,則徹底能夠並行,效率獲得了提高。示意圖以下:
Java 1.8 又對 ConcurrentHashMap 作了較大的改進,放棄了分段鎖的技術。結構和 Java 1.8 中的 HashMap 相似,採用的是數組+鏈表/紅黑樹來實現。爲了下降哈希衝突的成本,在鏈表長度超過 8 時,將鏈表轉換爲紅黑樹。使用 CAS 和 synchronized 解決併發問題,鎖住鏈表或者紅黑樹的頭節點,只要沒有哈希衝突,則不會出現併發問題。示意圖以下:
ConcurrentSkipListMap 保證有序的主要緣由是,底層使用的是跳錶這種數據結構,關於跳錶的介紹,你能夠查看數據結構中的內容。
Set 的兩個實現是 CopyOnWriteArraySet
和 ConcurrentSkipListSet
。
和前面說到的 CopyOnWriteArrayList 、ConcurrentSkipListMap 實現的原理相似。
隊列能夠從兩方面進行分類:
Java 中,單端隊列使用 queue 標識,雙端隊列使用 deque 標識。
經常使用的實現類有:ArrayBlockingQueue
、LinkedBlockingQueue
、PriorityQueue
。
其實現類是 ConcurrentLinkedQueue
其實現類是 LinkedBlockingDeque
其實現類是 ConcurrentLinkedDeque
使用其實都很是地簡單,就是入隊出隊之類的操做,這裏再也不贅述了。