Java併發程式設計(4)-同步容器以及併發容器介紹
文章目錄
更多關於Java併發程式設計的文章請點選這裡:
Java平臺的類庫包含了一個併發構造塊的豐富集合,本篇將介紹在併發程式設計中十分常用的兩種容器類,一是同步容器,二是併發容器,它們在某些特定的場合中可以用來替代我們常用的集合類,比如在高併發和多執行緒請求的情況下。
本篇總結自《Java併發程式設計實踐》第五章 構造塊 章節的內容,詳情可以閱讀該書。
一、同步容器
1.1、同步容器類介紹
同步容器類包括兩部分,一個是Vector和Hashtable,它們是早期JDK的一部分;另一個是它們的同系容器,在JDK1.2之後才被 引入的同步包裝類這些類是由Collections.synchronizedXxx工廠方法建立的
1.2、常用的同步容器類
常用的同步容器類有Collections.synchronizedMap、Collections.synchronizedList、Collections.synchronizedSet、Collections.synchronizedSortSet、Collections.synchronizedSortMap,它們分別是Map、List、Set、SortSet、SortMap的同步容器類。
1.3、使用同步容器類的方式
使用同步容器類的方式很簡單,如下程式碼就是使用了一個Map的同步類,對HashMap進行了包裝。
//建立一個同步Map容器類,包裝HashMap,使其執行緒安全
Map<String, String> synchronizedMap = Collections.synchronizedMap(new HashMap<String, String>());
接下來的操作和平時使用HashMap沒有什麼區別,常用的方法都沒有改變:
//儲存key-value
synchronizedMap.put("hello","world");
//通過key讀取value
String hello = synchronizedMap.get("hello");
System.out.println(hello);
//判斷key存在不存在,不存在則put
synchronizedMap.putIfAbsent("arong","java");
1.4、同步容器類的實現機制
同步容器類的實現機制前面已經說過,其實就是依靠synchronized塊對方法進行了同步,以阻止多個執行緒對一個方法進行併發的訪問,從而使其達到執行緒安全的效果,以下是Collections.synchronizedMap的相關程式碼,我們可以看出,每個方法都被上鎖了:
//static靜態化使使用者通過Collections類可直接獲取到它
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
//這一步返回一個SynchronizedMap類,並將Map作為引數傳入
return new SynchronizedMap<>(m);
}
//真正的同步容器類
private static class SynchronizedMap<K,V>
...
//所有的方法中都使用了synchronized對有狀態變數進行了同步
public int size() {
synchronized (mutex) {return m.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return m.isEmpty();}
}
public boolean containsKey(Object key) {
synchronized (mutex) {return m.containsKey(key);}
}
public boolean containsValue(Object value) {
synchronized (mutex) {return m.containsValue(value);}
}
public V get(Object key) {
synchronized (mutex) {return m.get(key);}
}
public V put(K key, V value) {
synchronized (mutex) {return m.put(key, value);}
}
public V remove(Object key) {
synchronized (mutex) {return m.remove(key);}
}
public void putAll(Map<? extends K, ? extends V> map) {
synchronized (mutex) {m.putAll(map);}
}
public void clear() {
synchronized (mutex) {m.clear();}
}
...
1.5、使用同步容器類的弊端
同步容器類雖然能保證在多執行緒請求的情況下容器資料的安全,但是弊端也是比較明顯的:所有的方法都是同步的,每次只能有一個執行緒獲得鎖,其他執行緒全部阻塞,這樣會導致效能問題,所以說,在對效能的要求不高,但是要求安全性良好的情況下,可以使用同步容器類,但是如果希望容器在併發請求時能同時接受多個執行緒的處理,並且也能保證執行緒安全的話,推薦使用下文介紹的併發容器類。
二、併發容器
2.1、什麼是併發容器類
Java5.0通過提供幾個併發的容器類來改進同步容器。同步容器通過對容器的所有狀態進行序列訪問。從而實現了它們的執行緒安全。這樣做的代價是削減了併發性,當多個執行緒共同競爭容器級的鎖時,吞吐量就會降低。另一方面,併發容器是為了多執行緒併發訪問而設計的。Java5.0添加了ConcurentHashMap來替代同步的HashMap實現;當多數操作為讀取操作時,CopyOnWriteArrayList是List相應的同步實現。新的ConcurrentMap介面還加入了常見覆合操作,比如“缺少即加入(put-if-absent)”、替換和條件刪除等。併發容器類位於java.util.concurrent,即併發包下。
2.2、CAS演算法
CAS是一種無鎖的非阻塞演算法,全稱為:Compare-and-swap(比較並交換),大致思路是:先比較目標物件現值是否和舊值一致,如果一致,則更新物件為新值;如果不一致,則表明物件已經被其他執行緒修改,直接返回。
2.3、ConcurrentHashMap
ConcurrentHashMap實現了HashTable的所有功能,執行緒安全,但卻在檢索元素時不需要鎖定,因此效率更高。ConcurrentHashMap的key 和 value都不允許null出現。原因在於ConcurrentHashMap不能區分出value是null還是沒有map上,相對的HashMap卻可以允許null值,在於其使用在單執行緒環境下,可以使用containKey(key)方法提前判定是否能map上,從而區分這兩種情況,但是ConcurrentHashMap在多執行緒使用上下文中則不能這麼判定。在併發程式設計中,常用來替換Collections.synchronizeHashMap。
2.4、CopyOnWriteArrayList
CopyOnWriteArrayList提供高效地讀取操作,使用在讀多寫少的場景。CopyOnWriteArrayList讀取操作不用加鎖,且是安全的;寫操作時,先copy一份原有資料陣列,再對複製資料進行寫入操作,最後將複製資料替換原有資料,從而保證寫操作不影響讀操作。在併發程式設計中,常常用來替換Collections.synchronizedList。