1. 程式人生 > >Java併發程式設計(4)-同步容器以及併發容器介紹

Java併發程式設計(4)-同步容器以及併發容器介紹

文章目錄


更多關於Java併發程式設計的文章請點選這裡:

Java併發程式設計實踐(0)-目錄頁


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