Java如何保證集合是執行緒安全的?(程式碼實踐拋磚引玉)
阿新 • • 發佈:2018-12-28
在Java中絕大部分的集合像什麼ArrayList、HashMap等絕大部分不是執行緒安全的。僅有的執行緒安全的實現,像HashTable、Vector等在效能上又不好。但是不要怕啊。我們大Java還有併發包(Java.util.concurrent)啊,為高度併發需求提供了全面安全的支援。
一、在傳統的集合框架中,如何解決執行緒安全問題。
當然,除了Hashtable等同步容器,我們可以使用同步包裝器建立一個執行緒安全的容器。但是這種方式用的是非常粗的同步方式,在高併發情況下,效能比較低下。
具體的位置如下
下面樓主寫了有一些實踐的程式碼:
package com.newframe.controllers.api; import java.util.*; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.SynchronousQueue; /** * 測試傳統執行緒安全的集合類 */ public class TestTraditionSyn { public static void main(String[] args) { /** * 1。在傳統的集合框架中。 * 除了Hashtable這個是執行緒安全的同步容器。 * 他的實現基本上就是將Put、get、size等各種 * 方法的操作加上"synchronized"。這就導致了所有的併發操作都在競爭同一把鎖 * 一個執行緒在進行同步操作時,其他執行緒只能等待,大大降低了併發執行的效率 * (當然這個因為同步的執行緒開銷較大,不推薦使用) * 還可以通過呼叫Collections工具類提供的包裝類。來構造執行緒安全的同步包裝容器,如下所示 */ //構造一個執行緒安全的List List<String> list = Collections.synchronizedList(new ArrayList<>()); list.add("hello"); list.add("world"); Iterator iterator1 = list.iterator(); while (iterator1.hasNext()){ System.out.println(iterator1.next()); } //構造一個執行緒安全的Map Map<String,Object> map1 = Collections.synchronizedMap(new HashMap<>()); map1.put("1","wang"); map1.put("2","dong"); map1.forEach((key,value) ->{ System.out.println("map1:" + key + "," + value); }); } }
二、重頭戲首選的肯定還是我們的Java併發包啊
具體位置如下:
下面樓主也寫了一些示範如何使用的簡單程式碼:
package com.newframe.controllers.api; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.SynchronousQueue; /** * 測試併發包中的集合類 */ public class TestConcurrentSyn { public static void main(String[] args) { /** * 2。併發包。在工作中,我們更加普遍的是選擇利用併發包提供 * 適合在高度併發的環境下使用 * 執行緒安全容器類 * 這個只要你是按照併發包的標準建立的集合,都是執行緒安全的。 */ //關於map的ConcurrentHashMap ConcurrentHashMap<String,Object> map2 = new ConcurrentHashMap<>(); map2.put("1","我是併發包直接構建的"); map2.put("2","我是執行緒安全的Map容器,ConcurrentHashMap"); map2.forEach((key,value) ->{ System.out.println("map2:" + key + "," + value); }); //關於list的CopyOnWriteArrayList CopyOnWriteArrayList<Integer> list2 = new CopyOnWriteArrayList<>(); list2.add(67612); list2.add(67362); list2.forEach(list ->{ System.out.println(list); }); /** * 併發包中的執行緒安全佇列 */ //ArrayBlockingQueue ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue(10); arrayBlockingQueue.add("1"); arrayBlockingQueue.add("3"); arrayBlockingQueue.forEach(queue->{ System.out.println(queue); }); } }
三、關於Java8以後的ConcurrentHashMap的一點思考。
ConcurrentHashMap的設計實現是一直都在不斷的演化,效能也是在不斷的提高。
早期的ConcurrentHashMap,其實現主要是基於:
- 分離鎖。在內部進行分段(Segment),裡面則是HashEntry的陣列,和HashMap類似,雜湊相同的條目也是以連結串列的形式存放。
- HashEntry內部使用volatile的value欄位來保證可見性。
那麼在Java8中,這個有什麼變化呢?
- 在結構上,雖然仍然有Segment定義,但是僅僅是為了給舊版本相容。初始化已經改成了Lazy-load的形式了,有效避免了初始化開銷。
- 資料儲存利用的是Volatile來保證可見性。如下圖:
好啦,關於這個裡面的東西太多了,還需要深入研究,當然,在工作中如果能明確應用場景,做出正確的選擇才是關鍵。