1. 程式人生 > >Java同步容器與併發容器

Java同步容器與併發容器

Java容器

Java提供了很多容器類,方便使用者使用。關鍵介面圖如下(圖片來源自The Java™ Tutorials),
Java core collection interfaces

  • Collection——集合框架結構的根節點。Java並沒有提供該介面的具體實現,但是提供了Collection子介面,比如Set,List的具體實現。
  • Set——沒有重複元素的集合。
  • List——有序的集合(或稱為序列)。可以包括重複元素。
  • Queue——用於儲存多個待處理的元素。典型地(但非必須),滿足FIFO(先進先出)規則。
  • Deque——儲存多個待處理的元素。支援先進先出(FIFO)和先進後出(LIFO)。雙向,元素可以在兩個方向新增或刪除。
  • Map——將鍵(key)對映到值(value)的結構。不能包含相同的key,每個key至多對映到一個value。

同步容器類

我們平時用的ArrayList,Queue,HashMap,都不是同步安全的。如果我們在多個執行緒中這些類的例項,就可能發生執行緒干擾或記憶體一致性錯誤。

我們可以使用同步訪問來解決這個問題,但是Java已經幫我們實現了,這就是接下來的同步容器類。同步容器類包括:

  • Vector和Hashtable,還有繼承Vector的Stack類。
  • Collections.synchronizedXxx等工廠方法建立的類。

這些類實現執行緒安全的方法是:將它們的成員變數設定為私有(狀態封裝),並對每個公有方法都進行同步,使得每次只有一個執行緒能訪問類的例項。

我們可以方便得使用這些同步容器來實現多個執行緒同步訪問。但是並不意味同步容器可以完全保證執行緒安全。考慮以下的例子,

    public static Object getLast(Vector vector) {
        int lastIndex = vector.size() - 1;
        return vector.get(lastIndex);
    }

    public static void deleteLast(Vector vector) {
        int lastIndex = vector.size() - 1;
        vector.remove(lastIndex);
    }

這兩個程式碼都會執行“先檢查再執行”操作。先獲得向量的大小,然後根據結果來得到或刪除最後一個元素。如果這兩個程式碼執行在兩個執行緒中,會發生以下的執行序列,

  1. getLast得到向量大小,為10,
  2. deleteLast得到向量大小,為10,
  3. deleteLast刪除最後一個元素,此時向量的大小為9
  4. getLast得到最後一個元素,get(9),但是由於deleteLast將最後一個元素刪除,此時長度為9,所以此時會出錯。

容器上常見的複合操作包括:迭代、跳轉以及條件運算,例如“沒有則新增”。在同步容器中,這些複合操作是執行緒安全的,但是在其他執行緒併發地修改容器時,就會發生意料之外的行為。比如上面的例子。

我們可以通過客戶端加鎖來解決該問題,使得getLast和deleteLast成為原子操作,確保vector的大小在呼叫size和get之間不會發生變化,示例程式碼如下:

    public static Object getLast(Vector vector) {
        synchronized (vector) {
            int lastIndex = vector.size() - 1;
            return vector.get(lastIndex);
        }
    }

    public static void deleteLast(Vector vector) {
        synchronized (vector) {
            int lastIndex = vector.size() - 1;
            vector.remove(lastIndex);
        }
    }

併發容器類

同步容器將所有對容器狀態的訪問都序列化,來實現執行緒安全性。這種方法的代價是嚴重降低併發性,當多個執行緒競爭容器的鎖時,吞吐量將嚴重降低。

Java 提供了多種併發容器來改善同步容器的效能。另外,併發容器是針對多個執行緒併發訪問設計的。

  • 替代Map的ConcurrentHashMap
  • 替代SortedMap的ConcurrentSkipListMap
  • 替代SortedSet的ConcurrentSkipListSet
  • 替代List的CopyOnWriteArrayList
  • ConcurrentLinkedQueue,先進先出佇列
  • BlockingQueue,貨站了Queue,支援可阻塞的插入和獲取等操作