CopyOnWriteArrayList 原理,优缺点

CopyOnWriteArrayList是Java并发包中提供的一种线程安全的ArrayList,其原理、优缺点可以归纳如下:

原理

CopyOnWriteArrayList的核心思想是"写时复制"(Copy-On-Write, COW),即在修改操作(添加、删除元素等)时,不是直接修改当前容器,而是先复制一份当前容器的副本,然后在副本上进行修改操作,最后再将原容器的引用指向修改后的新容器。这种机制保证了读操作不会受到写操作的阻塞,因此读操作是完全不用加锁的,性能较高。

优点

  1. 并发读性能高:由于读操作不需要加锁,且不会受到写操作的阻塞,因此在读操作远多于写操作的并发场景中,CopyOnWriteArrayList的性能表现非常出色。
  2. 线程安全:CopyOnWriteArrayList内部实现了线程安全的机制,无需外部同步,简化了并发编程的复杂性。
  3. 迭代器安全:CopyOnWriteArrayList提供了一个弱一致性的迭代器(Weakly Consistent Iterator),即在迭代过程中,即使列表发生了修改,迭代器仍然可以安全地进行,不会抛出ConcurrentModificationException异常。

缺点

  1. 内存开销大:由于每次写操作都会创建底层数组的一个副本,因此CopyOnWriteArrayList的内存开销相对较大。特别是在列表包含大量元素时,每次写操作都需要复制整个数组,这不仅会增加内存消耗,还可能导致Garbage Collection频繁发生,影响系统性能。
  2. 写操作性能低:由于每次写操作都需要复制整个底层数组,这使得写操作的时间复杂度是O(n),其中n是数组的大小。因此,在需要频繁写操作的场景下,CopyOnWriteArrayList的性能会非常低下。
  3. 数据实时性差:CopyOnWriteArrayList在每次写操作时都要复制整个数组,因此不适合用于需要实时读取最新数据的场景。在读取过程中,如果另一个线程完成了写操作,但由于读操作是在旧数组上进行的,读取线程依然会获取到写操作之前的数据。

使用场景

CopyOnWriteArrayList最适合的场景是读多写少的场景,如配置管理、事件监听器列表 等。在这些场景中,读操作非常频繁,而写操作相对较少。由于读操作无锁且非常高效,因此CopyOnWriteArrayList能够提供优异的性能。

实例

java 复制代码
import java.util.concurrent.CopyOnWriteArrayList;  
  
public class CopyOnWriteArrayListExample {  
    public static void main(String[] args) {  
        // 创建一个CopyOnWriteArrayList对象  
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();  
  
        // 添加元素到列表中  
        list.add("Alice");  
        list.add("Bob");  
        list.add("Charlie");  
  
        // 遍历并打印列表中的元素  
        System.out.println("Initial List:");  
        for (String element : list) {  
            System.out.println(element);  
        }  
  
        // 创建并启动多个线程,一个用于添加元素,一个用于删除元素  
        Thread thread1 = new Thread(new AddElementTask(list));  
        Thread thread2 = new Thread(new RemoveElementTask(list));  
  
        thread1.start();  
        thread2.start();  
  
        // 等待线程执行完毕  
        try {  
            thread1.join();  
            thread2.join();  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
  
        // 遍历并打印最终的列表内容  
        System.out.println("Final List:");  
        for (String element : list) {  
            System.out.println(element);  
        }  
    }  
  
    // 添加元素的线程任务  
    static class AddElementTask implements Runnable {  
        private CopyOnWriteArrayList<String> list;  
  
        public AddElementTask(CopyOnWriteArrayList<String> list) {  
            this.list = list;  
        }  
  
        @Override  
        public void run() {  
            for (int i = 1; i <= 5; i++) {  
                list.add("Element " + i);  
                System.out.println("Added Element " + i);  
                try {  
                    Thread.sleep(100); // 模拟一些延迟  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
  
    // 删除元素的线程任务  
    static class RemoveElementTask implements Runnable {  
        private CopyOnWriteArrayList<String> list;  
  
        public RemoveElementTask(CopyOnWriteArrayList<String> list) {  
            this.list = list;  
        }  
  
        @Override  
        public void run() {  
            for (int i = 1; i <= 3; i++) {  
                if (list.size() > 0) {  
                    String removedElement = list.remove(0);  
                    System.out.println("Removed " + removedElement);  
                }  
                try {  
                    Thread.sleep(200); // 模拟一些延迟  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
}

运行结果

bash 复制代码
Initial List:
Alice
Bob
Charlie
Removed Alice
Added Element 1
Added Element 2
Removed Bob
Added Element 3
Added Element 4
Removed Charlie
Added Element 5
Final List:
Element 1
Element 2
Element 3
Element 4
Element 5

与List性能对比(add)

java 复制代码
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListCompareListExample {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> copList = new CopyOnWriteArrayList<>();
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            copList.add(String.valueOf(i));
        }
        long end = System.currentTimeMillis();
        System.out.println("CopyOnWriteArrayList:"+(end - begin));


        List<String> list = new CopyOnWriteArrayList<>();
        long beginList = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            list.add(String.valueOf(i));
        }
        long endList = System.currentTimeMillis();
        System.out.println("List:"+(endList - beginList));
    }
}

运行结果

bash 复制代码
CopyOnWriteArrayList:2128
List:1936
相关推荐
yaosheng_VALVE6 小时前
探究全金属硬密封蝶阀的奥秘-耀圣控制
运维·eclipse·自动化·pyqt·1024程序员节
dami_king7 小时前
SSH特性|组成|SSH是什么?
运维·ssh·1024程序员节
一个通信老学姐5 天前
专业125+总分400+南京理工大学818考研经验南理工电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节
sheng12345678rui5 天前
mfc140.dll文件缺失的修复方法分享,全面分析mfc140.dll的几种解决方法
游戏·电脑·dll文件·dll修复工具·1024程序员节
huipeng9266 天前
第十章 类和对象(二)
java·开发语言·学习·1024程序员节
earthzhang20216 天前
《深入浅出HTTPS》读书笔记(19):密钥
开发语言·网络协议·算法·https·1024程序员节
爱吃生蚝的于勒7 天前
计算机基础 原码反码补码问题
经验分享·笔记·计算机网络·其他·1024程序员节
earthzhang20217 天前
《深入浅出HTTPS》读书笔记(20):口令和PEB算法
开发语言·网络协议·算法·https·1024程序员节
一个通信老学姐7 天前
专业140+总分410+浙江大学842信号系统与数字电路考研经验浙大电子信息与通信工程,真题,大纲,参考书。
考研·信息与通信·信号处理·1024程序员节
earthzhang20218 天前
《深入浅出HTTPS》读书笔记(18):公开密钥算法RSA(续)
网络·网络协议·算法·https·1024程序员节