併發集合

併發集合是執行緒安全集合的一般化,允許在併發環境中更廣泛地使用。

雖然執行緒安全集合具有安全元素新增或從多個執行緒中刪除,但它們不一定在同一上下文中具有安全迭代(一個可能無法在一個執行緒中安全地迭代集合,而另一個可能通過新增/來修改它刪除元素)。

這是使用併發集合的地方。

由於迭代通常是集合中幾個批量方法的基本實現,如 addAllremoveAll,或者集合複製(通過建構函式或其他方式),排序,…併發集合的用例實際上非常大。

例如,Java SE 5 java.util.concurrent.CopyOnWriteArrayList 是一個執行緒安全且併發的 List 實現,其 javadoc 狀態:

快照樣式迭代器方法在建立迭代器時使用對陣列狀態的引用。這個陣列在迭代器的生命週期中永遠不會改變,所以干擾是不可能的,並且保證迭代器不會丟擲 ConcurrentModificationException。

因此,以下程式碼是安全的:

public class ThreadSafeAndConcurrent {

public static final List<Integer> LIST = new CopyOnWriteArrayList<>();

public static void main(String[] args) throws InterruptedException {
    Thread modifier = new Thread(new ModifierRunnable());
    Thread iterator = new Thread(new IteratorRunnable());
    modifier.start();
    iterator.start();
    modifier.join();
    iterator.join();
}

public static final class ModifierRunnable implements Runnable {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 50000; i++) {
                LIST.add(i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public static final class IteratorRunnable implements Runnable {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 10000; i++) {
                long total = 0;
                for(Integer inList : LIST) {
                    total += inList;
                }
                System.out.println(total);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
}

關於迭代的另一個併發集合是 ConcurrentLinkedQueue ,它指出:

迭代器是弱一致的,在迭代器建立時或之後的某個時刻返回反映佇列狀態的元素。它們不會丟擲 java.util.ConcurrentModificationException,並且可能與其他操作同時進行。自建立迭代器以來佇列中包含的元素將只返回一次。

應檢查 javadoc 以檢視集合是否併發。iterator() 方法返回的迭代器的屬性(快速失敗弱一致,……)是要查詢的最重要的屬性。

執行緒安全但非併發的示例

在上面的程式碼中,將 LIST 宣告更改為

public static final List<Integer> LIST = Collections.synchronizedList(new ArrayList<>());

可以(並且在統計上將在大多數現代的多 CPU /核心架構上)導致異常。

來自 Collections 實用程式方法的同步集合對於元素的新增/刪除是執行緒安全的,但不是迭代(除非已經傳遞給它的底層集合)。