并发集合
并发集合是线程安全集合的一般化,允许在并发环境中更广泛地使用。
虽然线程安全集合具有安全元素添加或从多个线程中删除,但它们不一定在同一上下文中具有安全迭代(一个可能无法在一个线程中安全地迭代集合,而另一个可能通过添加/来修改它删除元素)。
这是使用并发集合的地方。
由于迭代通常是集合中几个批量方法的基本实现,如 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
实用程序方法的同步集合对于元素的添加/删除是线程安全的,但不是迭代(除非已经传递给它的底层集合)。