Java中的CopyOnWriteArrayList详解
引入
ArrayList单线程下是安全的 但是多线程下存在不安全的问题
多线程下是不安全的
例如: 我们开启多个线程向arraylist添加数据
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add("a");
System.out.println(list);
}).start();
}
}
}报出异常

解决这个问题我们可以使用集合工具类创建线程安全的ArrayList
List<String> strings = Collections.synchronizedList(new ArrayList<String>());
for (int i = 0; i < 101; i++) {
new Thread(()->{
strings.add("a");
System.out.println(strings);
}).start();
}
}
但加入synchronized此时效率较低
所以我们可以使用juc包中的 CopyOnWriterArrayList
CopyOnWriterArrayList
CopyOnWriteArrayList (拷贝写数组) 是ArrayList线程安全的集合
其中所有可变操作(add、set等等)都是通过对底层数组进行一次新的复制来实现的。
适合场景:读多、写少的并发场景
cow思想
它不存在“扩容”的概念,每次写操作(add 、remove)都要copy一个副本,在副本的基础上修改后改变array引用,所以称为“CopyOnWrite”,因此在写操作是加锁,并且对整个list的copy操作时相当耗时的,过多的写操作不推荐使用该存储结构。
读取的方法没有加锁 写的时候才加锁 所以性能 比 vector高
我们可以从源码中看出
public E get(int index) {
return get(getArray(), index);
}
final Object[] getArray() {
return array;
}
private E get(Object[] a, int index) {
return (E) a[index];
}读操作并没有加锁 这也是效率高的原因
add源码
写操作加入了 lock锁
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();
}
}(注:新版本jdk1.8后改成了synchronized 因为新版本对synchronized进行了升级提升了效率)
使用示例:
public class ListTest {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 101; i++) {
new Thread(()->{
list.add("a");
System.out.println(list);
}).start();
}
}
}
缺点:
内存问题:CopyOnWrite的写复制机制,在进行写操作的时候,内存里会同时驻存在两个对象的内存,旧的对象和新写入的对象(注意:在复制的时候只是复制容器里的引用,只是在写的时候会创建新对 象添加到新容器里,而旧容器的对象还在使用,所以有两份对象内存)。如果这些对象占用的内存比较大,比如说100M左右,那么再写入50M数据进去,内存就会占用150M,那么这个时候很有可能造成频繁的minor GC和major GC。
数据一致性问题:CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。
到此这篇关于Java中的CopyOnWriteArrayList详解的文章就介绍到这了,更多相关CopyOnWriteArrayList详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
Java常见启动命令-jar、-server和-cp详细比较
这篇文章主要给大家介绍了关于Java常见启动命令-jar、-server和-cp详细比较的相关资料,该文总结了常归的jar包的启动方式,并分析各种启动方式的区别,需要的朋友可以参考下2023-07-07
IntelliJ IDEA运行bat脚本,自动taskkill端口进程
这篇文章主要介绍了IDEA里面无法运行bat文件的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2023-11-11
从零搭建Spring Boot脚手架整合OSS作为文件服务器的详细教程
这篇文章主要介绍了从零搭建Spring Boot脚手架整合OSS作为文件服务器的详细教程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-08-08
线程池之newFixedThreadPool定长线程池的实例
这篇文章主要介绍了线程池之newFixedThreadPool定长线程池的实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-06-06


最新评论