工作原理
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的优势在于可以保证线程安全的同时,不阻塞读操作,但是这仅限于读多写少的情况,在写多读少的情况下,或者写入的对象占用内容较大时,不建议使用。