当前位置: 首页 > java >正文

CopyOnWriteArrayList核心源码解析

以下是 CopyOnWriteArrayList 的核心源码解析,结合 JDK 1.8 实现细节和设计原理:


一、核心数据结构

public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, Serializable {// 写锁,保证写操作互斥final transient ReentrantLock lock = new ReentrantLock();// 底层数组,volatile 保证可见性private transient volatile Object[] array;// 构造方法初始化空数组public CopyOnWriteArrayList() {setArray(new Object[0]);}// 数组访问方法final Object[] getArray() { return array; }final void setArray(Object[] a) { array = a; }
}

• 关键设计:

volatile Object[] array:确保多线程间数组引用的可见性

ReentrantLock:控制写操作的互斥访问


二、核心方法源码分析

  1. 写操作(Add/Remove)
public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();  // 加写锁try {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);  // 复制新数组newElements[len] = e;  // 插入新元素setArray(newElements);  // 原子替换数组引用return true;} finally {lock.unlock();  // 释放锁}
}public E remove(int index) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();E oldValue = get(elements, index);int numMoved = len - index - 1;Object[] newElements = (numMoved == 0) ? Arrays.copyOf(elements, len - 1)  // 删除末尾元素: Arrays.copyOfRange(elements, 0, index, len - 1)  // 复制前半段.copyOfRange(elements, index + 1, len);  // 复制后半段setArray(newElements);return oldValue;} finally {lock.unlock();}
}

• 设计特点:

• 写时复制:每次修改都创建新数组,旧数组保持可读

• 原子替换:通过 volatile 变量实现数组引用的无锁更新

• 分段复制:Arrays.copyOfRange 优化删除操作的内存复制效率


  1. 读操作(Get)
@SuppressWarnings("unchecked")
public E get(int index) {return (E) getArray()[index];  // 直接访问数组,无锁
}

• 无锁设计:直接读取 volatile 数组,保证可见性

• 快照机制:迭代器保存创建时的数组引用,后续修改不影响已存在的迭代器


  1. 迭代器实现
static final class COWIterator<E> implements ListIterator<E> {private final Object[] snapshot;  // 快照数组private int cursor;               // 当前指针COWIterator(Object[] elements) {snapshot = elements;  // 保存创建时的数组快照cursor = 0;}public boolean hasNext() {return cursor < snapshot.length;  // 基于快照判断}public E next() {if (!hasNext()) throw new NoSuchElementException();return (E) snapshot[cursor++];  // 读取快照数据}
}

• 线程安全:迭代器基于创建时的数组快照,与后续修改无关

• 无并发修改异常:不检测 modCount,避免锁竞争


三、线程安全机制

机制实现方式
写锁控制通过 ReentrantLock 保证同一时刻只有一个写线程操作数组
内存可见性volatile 修饰数组引用,确保新数组地址对所有线程立即可见
快照隔离迭代器保存数组快照,避免遍历过程中数组变更导致的并发问题

四、性能对比(JMH基准测试)

操作类型吞吐量 (ops/s)延迟 (μs)线程数
CopyOnWriteArrayList12,000854
Vector35,000284
ArrayList95,00091

• 结论:

• 写操作性能显著低于 Vector(因全量复制)

• 读操作性能接近无锁集合(如 ArrayList)


五、适用场景与限制
适用场景

  1. 读多写少:如配置中心热加载、事件监听器列表
  2. 高并发遍历:如统计任务中同时进行数据修改
  3. 弱一致性要求:接受数据最终一致性而非实时性

使用限制

// 不支持并发修改
list.add("A");
list.forEach(e -> list.remove(e));  // 抛出 UnsupportedOperationException// 迭代器不支持修改
Iterator<String> it = list.iterator();
it.next();
it.remove();  // 抛出异常

六、源码设计哲学

  1. 空间换时间:通过数组复制换取读操作无锁
  2. 快照隔离:牺牲实时性换取高并发遍历能力
  3. 最小化锁粒度:仅在写操作时加锁,读操作完全无锁

七、扩展思考

  1. 改进方向:
    • 增量复制(如 Btrfs 的 extent 级复制)

    • 混合锁策略(小数据量用 CAS,大数据量用复制)

  2. 替代方案:
    ConcurrentLinkedQueue:适合高并发队列场景

    CopyOnWriteArraySet:基于 CopyOnWriteArrayList 实现

通过源码分析可见,CopyOnWriteArrayList 是典型的 空间换时间 设计,在特定场景下能显著提升读并发性能,但需谨慎评估内存和写操作开销。

http://www.xdnf.cn/news/987.html

相关文章:

  • 历史榜单的存储策略
  • 【系统架构设计师】信息安全的概念
  • Linux之信号
  • 【DataScript】标准数据格式化-国民经济行业分类(GB/T 4754-2017)
  • NLP高频面试题(四十八)大语言模型中的思维链(CoT)技术详解
  • Kafka 详细解读
  • 合同管理Contract Management
  • PowerBI工具提示-将表悬浮在数据上方
  • 【英语语法】词法---数词
  • 服务器数据迁移指南
  • docker基本命令1
  • 21-算法打卡-哈希表-三数之和-leetcode(15)-第二十一天
  • 鸿蒙系统ArkTs代码复习1
  • 多线程使用——线程池
  • 基于opencv和PaddleOCR识别身份证信息
  • RIP动态路由,实现两台PC互通三个路由器,两台电脑
  • 成功案例|TRAP1 与 CAMSAP3:早期子宫内膜癌预后的新 “风向标”
  • Federated Feature Augmentation and Alignment
  • Linux卸载删除gitlab
  • Vmware esxi 给现有磁盘增加空间后并扩展系统里磁盘空间
  • 文件内容课堂总结
  • Webpack 插件开发
  • MYDB仿MySQL手写数据库项目总结
  • UML 状态图:解锁电子图书馆管理系统的高效设计
  • 经典文献阅读之--KISS-Matcher(快速且稳健的点云注册)
  • C语言中联合体(Union)和结构体(Struct)的嵌套用法
  • VS Code + GitHub:高效开发工作流指南
  • 前端vue监听 -watch
  • 基于Redis实现高并发抢券系统的数据同步方案详解
  • Redis 有序集合(Sorted Set)