并发集合

并发集合是线程安全集合的一般化,允许在并发环境中更广泛地使用。

虽然线程安全集合具有安全元素添加或从多个线程中删除,但它们不一定在同一上下文中具有安全迭代(一个可能无法在一个线程中安全地迭代集合,而另一个可能通过添加/来修改它删除元素)。

这是使用并发集合的地方。

由于迭代通常是集合中几个批量方法的基本实现,如 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 实用程序方法的同步集合对于元素的添加/删除是线程安全的,但不是迭代(除非已经传递给它的底层集合)。