MST
星途 面试题库

面试题:Java并发数据结构CopyOnWriteArrayList的适用场景及实现原理

说明Java的CopyOnWriteArrayList适用于哪些并发场景,并且详细解释它是如何实现线程安全的,包括写操作和读操作的具体实现方式及对性能的影响。
35.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

适用并发场景

  1. 读多写少场景:CopyOnWriteArrayList 非常适合读操作远多于写操作的并发场景。例如,在一些配置信息加载、日志记录等场景中,数据一旦初始化后很少发生变化,但会被大量线程频繁读取。

线程安全实现

  1. 写操作实现
    • 当执行写操作(如 addremove 等方法)时,CopyOnWriteArrayList 会先创建一个原数组的副本。例如,add 方法实现如下:
    public boolean add(E e) {
        synchronized (lock) {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        }
    }
    
    • 这里使用 synchronized 关键字进行同步,保证同一时间只有一个线程能进行写操作。然后创建原数组的副本,在副本上进行写操作,最后将原数组引用指向新的副本数组。
  2. 读操作实现
    • 读操作(如 get 方法)则直接读取当前数组,不需要加锁。例如 get 方法实现如下:
    public E get(int index) {
        return get(getArray(), index);
    }
    private E get(Object[] a, int index) {
        return (E) a[index];
    }
    
    • 因为读操作不涉及修改数组内容,所以不需要同步,多个线程可以并发读取。

对性能的影响

  1. 写操作性能影响:写操作由于需要创建数组副本,开销较大。每次写操作都要进行数组复制,这涉及内存分配和数据复制,在写操作频繁的场景下,性能会受到明显影响。
  2. 读操作性能影响:读操作性能较高,因为无需加锁,避免了锁竞争带来的开销,特别适合高并发读的场景。但是,由于读的是一个快照数据,如果写操作频繁,读的数据可能不是最新的,但在允许一定数据延迟的场景下可以接受。