併發集合
併發集合是執行緒安全集合的一般化,允許在併發環境中更廣泛地使用。
雖然執行緒安全集合具有安全元素新增或從多個執行緒中刪除,但它們不一定在同一上下文中具有安全迭代(一個可能無法在一個執行緒中安全地迭代集合,而另一個可能通過新增/來修改它刪除元素)。
這是使用併發集合的地方。
由於迭代通常是集合中幾個批量方法的基本實現,如 addAll
,removeAll
,或者集合複製(通過建構函式或其他方式),排序,…併發集合的用例實際上非常大。
例如,Java SE 5 java.util.concurrent.CopyOnWriteArrayList
是一個執行緒安全且併發的 Lis
t 實現,其 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
實用程式方法的同步集合對於元素的新增/刪除是執行緒安全的,但不是迭代(除非已經傳遞給它的底層集合)。