工作原理
CopyOnWriteArrayList 是java.util.concurrent的并发类,线程安全,遵循写时复制的原则(CopyOnWrite)。通过“写入时复制”(Copy-On-Write, COW)的策略来保证集合的线程安全,适用于读多写少的并发场景。
CopyOnWriteArrayList 在每次修改操作时(如添加、删除、设置等),都会通过复制底层数组来创建一个新的数组,并在这个新数组上进行修改,然后将原数组的引用指向这个新数组。这样,在修改过程中,迭代器和其他分割器(spliterator)可以继续安全地遍历原数组,因为它们不会受到修改操作的影响。
优点
- 线程安全:通过内部机制保证了线程安全,无需外部同步。
- 读操作性能高:读操作不会加锁,且多个读操作可以并发执行,因此读操作的性能非常高。
缺点
- 内存占用高:每次修改操作都需要复制整个底层数组,这会导致大量的内存占用,特别是在数组较大且修改频繁时。
- 写操作性能低:写操作(添加、删除、设置等)涉及数组的复制,因此写操作相对较慢,且写操作的性能会随着数组容量的增加而下降。
使用场景
- 读多写少的并发场景。
- 遍历操作远多于修改操作的场景。
- 在需要线程安全的列表,但又不想因为使用
Vector
而导致性能下降时。
示例代码
import java.util.concurrent.CopyOnWriteArrayList;
public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
// 线程1添加元素
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
list.add("Element " + i);
try {
Thread.sleep(100); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 线程2遍历元素
Thread t2 = new Thread(() -> {
for (String s : list) {
System.out.println(s);
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
总结
CopyOnWriteArrayList的优势在于可以保证线程安全的同时,不阻塞读操作,但是这仅限于读多写少的情况,在写多读少的情况下,或者写入的对象占用内容较大时,不建议使用。