1. 为什么需要线程安全集合
在多线程并发编程中,集合操作的安全性问题就像在繁忙的十字路口没有交通信号灯一样危险。当多个线程同时读写同一个ArrayList时,就像多辆车同时争抢同一个车道,很容易发生数据错乱甚至程序崩溃。我曾在实际项目中遇到过因为未使用线程安全集合导致的诡异bug——用户数据偶尔会莫名其妙地丢失,排查了整整两天才发现是ArrayList的并发修改问题。
Java提供了多种线程安全集合解决方案,其中CopyOnWriteArrayList采用了一种"写时复制"的独特思路。它的核心思想很简单:每当需要修改集合时(增删改操作),不是直接操作原数组,而是先复制一份全新的数组副本,在副本上完成修改后,再将集合的引用指向这个新数组。这种机制就像公司发布新版员工手册——不会直接在旧手册上涂改,而是先复印一份,修改后再通知所有人换用新手册。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. CopyOnWriteArrayList的实现原理
2.1 底层数据结构剖析
打开CopyOnWriteArrayList的源码,你会发现它内部维护着一个volatile修饰的数组引用:
java复制private transient volatile Object[] array;
这个volatile关键字确保了多线程环境下数组引用的可见性——当某个线程修改了array引用指向新数组时,其他线程能立即看到这个变化。每次写操作都会创建一个新数组,这解释了为什么它叫"Copy-On-Write"。
2.2 写操作的具体流程
以add方法为例,我们看看写操作如何保证线程安全:
java复制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();
}
}
这个过程就像厨师在餐厅更新菜单:
- 先锁住厨房门(获取锁)
- 复印当前菜单(复制原数组)
- 在新菜单上添加菜品(操作新数组)
- 将新菜单挂到墙上(替换引用)
- 打开厨房门(释放锁)
2.3 读操作的特别之处
读操作完全不需要加锁,直接访问当前数组引用:
java复制
