Java 并发集合:CopyOnWrite 写时复制集合介绍

在 Java 的并发编程中,传统的集合类(如 ArrayListHashMap 等)在多线程环境下使用时,可能会导致数据不一致和并发异常的问题。为了解决这个问题,Java 提供了一些并发集合类,其中之一就是 CopyOnWriteArrayList

1. 什么是 CopyOnWriteArrayList?

CopyOnWriteArrayList 是一种实现了 List 接口的线程安全集合,属于 Java 的并发集合(java.util.concurrent 包)。它的核心特点是:在对集合进行修改(比如添加、删除元素)时,会将当前集合的快照复制一份,然后在复制的快照上进行修改。因此,这种策略被称为“写时复制”。

这种设计理念有几个明显的优势: - 读操作的要素不需要加锁,读取数据时不受写操作的影响,所以读性能非常高。 - 在写操作非常少、读操作非常多的场景下,使用 CopyOnWriteArrayList 可以有效降低锁的竞争,提高整体并发性能。

2. CopyOnWriteArrayList 的使用示例

下面是 CopyOnWriteArrayList 的一个简单示例,展示了如何在多线程环境下使用它:

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteExample {
    public static void main(String[] args) {
        // 创建一个 CopyOnWriteArrayList
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

        // 向集合中添加元素
        list.add("A");
        list.add("B");
        list.add("C");

        // 创建多个线程,模拟并发读取
        Runnable readTask = () -> {
            for (String item : list) {
                System.out.println(Thread.currentThread().getName() + "读取: " + item);
                try {
                    Thread.sleep(100); // 模拟读取延迟
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        };

        // 创建多个线程,模拟并发添加
        Runnable writeTask = () -> {
            for (int i = 0; i < 3; i++) {
                String newItem = "D" + i;
                list.add(newItem);
                System.out.println(Thread.currentThread().getName() + "添加: " + newItem);
                try {
                    Thread.sleep(150); // 模拟写入延迟
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        };

        // 启动多个读取线程
        Thread readThread1 = new Thread(readTask, "读取线程1");
        Thread readThread2 = new Thread(readTask, "读取线程2");
        readThread1.start();
        readThread2.start();

        // 启动多个写入线程
        Thread writeThread1 = new Thread(writeTask, "写入线程1");
        Thread writeThread2 = new Thread(writeTask, "写入线程2");
        writeThread1.start();
        writeThread2.start();

        try {
            readThread1.join();
            readThread2.join();
            writeThread1.join();
            writeThread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3. 解释代码

在上述示例中,我们创建了一个 CopyOnWriteArrayList 并添加了初始元素 'A', 'B', 'C'。随后,我们定义了两个任务,一个是读取列表内容的线程,另一个是往列表中添加新元素的线程。

  1. 读取任务 (readTask): 这个任务循环读取集合中的元素,并打印当前线程的名称和读取的元素。由于 CopyOnWriteArrayList 的特性,读取时不需要额外的同步。

  2. 写入任务 (writeTask): 这个任务循环添加元素到集合中,每次添加都会创建集合的一个新副本。

在多线程的情况下,读取线程和写入线程会同时运行,读取线程能够读取到添加之前的元素,以及在添加完成后新添加的元素。

结论

CopyOnWriteArrayList 是一个在多读少写场景下非常优雅的解决方案。虽然写操作可能会造成额外的性能开销(因为每次写入都要复制整个数组),但在读操作频繁的情况下,CopyOnWriteArrayList 提供了良好的性能表现。对于需要保持线程安全且读写操作不平衡的应用场景,选择 CopyOnWriteArrayList 在许多情况下都是一个明智的选择。

点赞(0) 打赏

微信小程序

微信扫一扫体验

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部