Java 中的線程安全容器

1、同步容器

經常使用的一些容器例如 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();
        }
    }
}

2、併發容器

Java 中還提供了一系列併發容器,相比於同步容器,其性能更好。併發容器共分爲了四類:List、Map、Set、Queue。spa

1. List

List 中一個最主要的實現類是 CopyOnWriteArrayList ,CopyOnWrite,即寫時複製,這樣的好處是讀操做是無鎖的。線程

其實現原理是內部維護了一個數組,內部變量 array 指向了這個數組。須要寫時,並非在原數組上操做,而是將數組複製一份,在拷貝的數組中進行寫。完成後,將 array 指向新的數組。這樣一來,讀寫之間不互斥,效率獲得了很大的提高。3d

須要注意的是 CopyOnWriteArrayList 適用於讀多寫少的場景,而且須要接受讀寫的暫時不一致,由於在寫的時候,並行的讀操做可能並不能立刻看到寫的結果。code

2. Map

Map 的兩個主要實現類是 ConcurrentHashMapConcurrentSkipListMap,二者主要的區別是:前者是無序的,後者是有序的。

2.1 ConcurrentHashMap

在 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 解決併發問題,鎖住鏈表或者紅黑樹的頭節點,只要沒有哈希衝突,則不會出現併發問題。示意圖以下:

2.2 ConcurrentSkipListMap

ConcurrentSkipListMap 保證有序的主要緣由是,底層使用的是跳錶這種數據結構,關於跳錶的介紹,你能夠查看數據結構中的內容。

3. Set

Set 的兩個實現是 CopyOnWriteArraySetConcurrentSkipListSet

和前面說到的 CopyOnWriteArrayList 、ConcurrentSkipListMap 實現的原理相似。

4. Queue

隊列能夠從兩方面進行分類:

  • 單端和雙端:單端隊列指的是隻能在隊首出隊,隊尾出隊,而雙端隊列指的是隊首和隊尾都可入隊和出隊。
  • 阻塞和非阻塞:阻塞隊列指的是,當隊列滿的時候,入隊列阻塞;當隊列空的時候,出隊列阻塞。

Java 中,單端隊列使用 queue 標識,雙端隊列使用 deque 標識。

4.1 單端阻塞隊列

經常使用的實現類有:ArrayBlockingQueueLinkedBlockingQueuePriorityQueue

4.2 單端非阻塞隊列

其實現類是 ConcurrentLinkedQueue

4.3 雙端阻塞隊列

其實現類是 LinkedBlockingDeque

4.4 雙端非阻塞隊列

其實現類是 ConcurrentLinkedDeque

使用其實都很是地簡單,就是入隊出隊之類的操做,這裏再也不贅述了。

相關文章
相關標籤/搜索