Java中的ArrayList和CopyOnWriteArrayList

Java ArrayList线程安全解决方案:从synchronizedList到CopyOnWriteArrayList 在Java并发编程中,线程安全是保证多线程环境下数据一致性的核心概念。其原理在于通过同步机制控制对共享资源的访问,确保操作的原子性、可见性和有序性。这一技术价值在于防止数据竞争、脏读和程序崩溃,是构建高可靠分布式系统的基石。典型应用场景包括电商库存管理、实时数据采集和金融交易系统等。针对ArrayList这类非线程安全集合,开发者常面临ConcurrentModificationException和性能瓶颈等挑战。本文深入剖析Collections.synchronizedList的迭代器陷阱,并解读Cop 阅读详情

ArrayList是List接口的基本实现之一,它是Java Collections Framework的一部分。我们可以使用迭代器遍历ArrayList元素。

我们看看一下ArrayList的示例程序:

package com.roin.concurrent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ConcurrentListExample {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        list.add("E");

        // get the iterator
        Iterator<String> it = list.iterator();
        //manipulate list while iterating
        while(it.hasNext()){
            System.out.println("list is:"+list);
            String str = it.next();
            System.out.println(str);
            if(str.equals("B"))
                list.remove("E");
            if(str.equals("C"))
                list.add("C found");
            //below code don't throw ConcurrentModificationException
            //because it doesn't change modCount variable of list
            if(str.equals("D"))
                list.set(1, "D");
        }
    }

}

运行结果:

D:\Java\jdk1.8.0_102\bin\java -Didea.launcher.port=7539 "-Didea.launcher.bin.path=D:\Program Files\JetBrains\IntelliJ IDEA 15.0\bin" -Dfile.encoding=UTF-8 -classpath "D:\Java\jdk1.8.0_102\jre\lib\charsets.jar;D:\Java\jdk1.8.0_102\jre\lib\deploy.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\access-bridge.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\cldrdata.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\dnsns.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\jaccess.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\jfxrt.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\localedata.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\nashorn.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\sunec.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\sunjce_provider.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\sunmscapi.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\sunpkcs11.jar;D:\Java\jdk1.8.0_102\jre\lib\ext\zipfs.jar;D:\Java\jdk1.8.0_102\jre\lib\javaws.jar;D:\Java\jdk1.8.0_102\jre\lib\jce.jar;D:\Java\jdk1.8.0_102\jre\lib\jfr.jar;D:\Java\jdk1.8.0_102\jre\lib\jfxswt.jar;D:\Java\jdk1.8.0_102\jre\lib\jsse.jar;D:\Java\jdk1.8.0_102\jre\lib\management-agent.jar;D:\Java\jdk1.8.0_102\jre\lib\plugin.jar;D:\Java\jdk1.8.0_102\jre\lib\resources.jar;D:\Java\jdk1.8.0_102\jre\lib\rt.jar;F:\Javapackage\dubbo\concurrent\out\production\concurrent;D:\Program Files\JetBrains\IntelliJ IDEA 15.0\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain com.roin.concurrent.ConcurrentListExample
list is:[A, B, C, D, E]
A
list is:[A, B, C, D, E]
B
list is:[A, B, C, D]
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
	at java.util.ArrayList$Itr.next(ArrayList.java:851)
	at com.roin.concurrent.ConcurrentListExample.main(ConcurrentListExample.java:21)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Process finished with exit code 

 我们运行上面的程序会报ConcurrentModificationException异常,发生这种情况是因为ArrayList迭代器在设计上是快速失效的。这意味着一旦创建了迭代器,如果修改了ArrayList,则会抛出ConcurrentModificationException。

我们注意到异常是由Iterator next()方法抛出的,现在我们看看源码中 next()里做了什么?

@SuppressWarnings("unchecked")
public E next() {
    checkForComodification();
    int i = cursor;
    if (i >= size)
        throw new NoSuchElementException();
    Object[] elementData = ArrayList.this.elementData;
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

  再看checkForComodification方法里面做了什么?

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

expectedModCount是我们创建迭代器时初始化的迭代器变量,而里modCount是保存修改次数。我们每次使用ArrayList的add,remove或trimToSize都会修改这个值,当检查到不一致时就会抛出上述异常,因此ArrayList是非安全的。

可以一看一下是在创建迭代器时初始化值的expectedModCount是在创建迭代器时初始化值的

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

有时我们想要添加或删除列表中的元素,那么我们应该使用并发集合类 - CopyOnWriteArrayList。这是java.util.ArrayList的线程安全变体,其中所有可变操作(add,set等)都是通过创建底层数组的新副本来实现的。

CopyOnWriteArrayList为处理引入了额外的性能消耗,但与遍历操作的次数相比,修改的次数很少时,它非常有效。

如果我们把上面程序ArrayList改成CopyOnWriteArrayList,那么我们不会得到任何异常。

现在看运行结果:


我们可以看,list中的元素被改变了,但是迭代的元素还是原来list中的值。

说明:CopyOnWriteArrayList为什么能做到迭代的同时还能进行数据修改,首先根据其名字CopyOnWrite,也就是在写的时候会拷贝一个新的Object数组,然后对新数组进行修改,修改完成后,再将旧数组的引用指向这个新的数组,那么读呢?读还是在旧的数组上读,可以看出往数组里面写和读之间是有时间差的,不是实时的。

我们看看源码中几个关键点:

1.数组是使用volatile修饰的,也就意味着array数组一旦修改,其他线程迭代该数组马上就能拿到值。

private transient volatile Object[] array;

2.读并未加锁,多个线程读操作不会有线程安全问题,多个线程读取会,读取到旧的数据。

@SuppressWarnings("unchecked")
private E get(Object[] a, int index) {
    return (E) a[index];
}

3.写操作使用了锁,我们看看add方法,其它类似

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();
    }
}

这里加锁的目的是多线程防止拷贝多个数组,我们看到拷贝工作流程,

首先获取原来数组长度,然后拷贝产生一个大小为原来数组+1个元素的新数组,

然后在新数组的最后一个索引位置设置我们要添加的值,

最后把array的引用指向新的数组。

看下setArray方法

/**
 * Sets the array.
 */
final void setArray(Object[] a) {
    array = a;
}

总结一下吧:

ArrayList在迭代的过程中,如果发生增加,删除等导致modCount发生变化的操作时会抛出异常。

CopyOnWriteArrayList,在多线程环境中读多写少场景是比较有效的,它使用写时变更到新数组,然后修改引用指向,读还是在旧数组上读,实现读写分离,写和读有所延迟,数据不保证实时一致性,只能保证最终一致性,另外需要注意这种拷贝对象会耗费不少的内存。









Uniapp返回上一页并刷新:亲测有效的 goBack 实现技巧! 这个技巧在实际开发中非常常用,特别适合表单提交后返回列表刷新这类场景。比起依赖onShow或复杂的vuex状态同步,这种方式更直接、好维护,也不容易出 bug。 阅读详情

相关推荐

产品必备技能(十):如何绘制原型图?先要绘制页面流程图+页面原型图!附实操原型图、流程图实例(抖音)

文章目录目标题目实操步骤模板实操【抖音】APP的视频发布功能页面流程绘制页面流程图合格标准绘制页面原型图合格标准 目标 新入行的产品经理都对原型抱有向往的心境,但是有经验的产品经理不会一上来就画原型,而是先画页面流程再来画原型。通过学习,你将学会如何用页面流程图规划用户行为路径、如何借助原型识别问题、减少风险、有效沟通。 题目 任意选择一款你喜欢的产品,体验某个功能,下载并梳理此功能至少5个以上的页面流程跳转点(后面会解释),还原2 - 3个主要界面的低保真原型图。 实操步骤 可使用手绘以及Process

汪雯琦的博客 4841

Java中的List集合:从ArrayList到CopyOnWriteArrayList

在Java集合框架中,List接口是最常用的接口之一。List表示一个有序的集合,允许重复元素,并提供了基于索引的操作。add(E e): 添加元素到列表末尾: 在指定位置插入元素: 获取指定位置的元素: 移除指定位置的元素: 替换指定位置的元素size(): 返回列表的大小Java提供了多种List实现,每种实现都有其特定的用途和性能特征。

2303_76892351的博客 1542

【多模态大模型】GLIP:零样本目标检测新范式与视觉语言理解

本文深入解析了多模态大模型GLIP在零样本目标检测和视觉语言理解领域的创新应用。GLIP通过统一目标检测与自然语言理解任务,实现了零样本学习能力,能够精准定位复杂文本描述对应的图像区域。文章详细介绍了GLIP的核心技术、数据策略及实战表现,并对比了与其他多模态模型的差异,为AI视觉理解提供了新范式。

weixin_29186249的博客 263

ArrayList数据结构深挖(五)之CopyOnWriteArrayList

是Java并发包()中的一个线程安全的List实现。它的核心思想是“写时复制”(Copy-On-Write),即在修改操作(如添加、删除、修改元素)时,会先复制一份底层数组的副本,然后在副本上进行修改,最后将副本替换回原数组。这种设计使得读操作可以在不加锁的情况下进行,从而提高了读操作的性能。通过“写时复制”机制和volatile关键字保证了并发安全性,同时无锁读操作提高了读性能。尽管写操作的开销较大,但在读多写少的场景中,它仍然是一个高效的选择。希望本文能帮助你深入理解的设计原理和适用场景!

qq_61302385的博客 418

ZEMAX知识点:坐标间断面(coordinate break)

zemax知识点:坐标间断面

weixin_47638568的博客 1万+

高通camera调试经验总结

修改AF对焦模式 adb shell setprop vendor.debug.camera.af.manual 2 //使能手动对焦 adb shell setprop vendor.debug.camera.af.manual 0 //使能自动对焦

weixin_43381364的博客 1万+

CopyOnWriterArrayList 详解

在 ArrayList 的类注释上,JDK 就提醒了我们,如果要把 ArrayList 作为共享变量的话,是线程不安全的,推荐我们自己加锁或者使用 Collections.synchronizedList 方法,其实 JDK 还提供了另外一种线程安全的 List,叫做 CopyOnWriteArrayList。......

GeGe&YoYo的博客 1万+

【零基础学习CAPL】——CAN报文的发送(CRC——ChecSum值)

本章主要介绍带有CRC值的报文发送一般报文可使用CANoe的IG模块直接发送,但带有CRC值的报文就只能编写脚本进行发送,以下详细讲解在配合Panel面板情况下,如何进行带有循环冗余码的报文进行报文的发送。

LOVE135149的博客 1913

CopyOnWriteArrayList详解

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_33979363的博客 404

Microsemi Libero系列教程(一)——Libero开发环境介绍、下载、安装与注册

前言 相比与Xilinx和Altera在国内的市场,Microsemi的FPGA在国内应用很少很少,网上几乎没有详细的教程,刚开始使用时,遇到了各种问题,自己也走了不少弯路。本系列教程以Libero V11.8.2.4,SmartFusion系列A2F200M3F FPGA芯片为例,将详细介绍Libero SoC软件的基本使用,及使用过程中遇到一些问题的解决方法。 Libero 简介 关于Lib...

whik1194的博客 3万+

SAP FICO 内部往来对账功能开发说明书(包括测试样例、程序代码仅作参考,不保证一定可以运行)

SAP FICO 内部往来对账功能开发说明书(包括开发源代码、测试样例、FS)

我好像病了的博客 1223

并发的ArrayList——CopyOnWriteArrayList 源码阅读

在ArrayList类的注释中,就已经提到了,ArrayList是线程不安全的类,不建议作为线程的共享变量使用。那么,是否有线程安全的List呢?当然有,那就是Vector……才怪Vector类的方法仅仅是将所有的方法都加上的synchronized关键字,强制将并发转为串行,效率低下。好在,JDK在java.util.concurrent包(即常说的JUC)下,提供了一个线程安全的另一个List,即CopyOnWriteArrayList。PS..类注释中是这样说的try{...

ironmanoOOoo的博客 574

【CANoe】CANoe的 I/O功能-以VN1640A为例

Canoe的 I/O功能-以VN1640A为例

星Yeah 的博客 7875

Java容器-ArrayList、Vector、LinkedList、CopyOnWriteArrayList

一、概览 容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。 Collection 1. Set TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。 HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也

兴趣是最好的老师 290

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Java并发List实战:CopyOnWriteArrayList原理与ArrayList常见面试题

本文讨论了ArrayList的线程安全问题及其解决方案。通过多线程并发测试验证了ArrayList的非线程安全性,指出了可能出现的null值、索引越界和size计数错误问题。提供了三种线程安全解决方案:Collections.synchronizedList()、CopyOnWriteArrayList和使用Vector。重点分析了CopyOnWriteArrayList的实现原理,包括volatile修饰数组、写操作加锁及写时复制机制。还解析了ArrayList的扩容机制(默认容量10,扩容1.5倍)。文

程序员天天困的博客 2699

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

java集合框架-----List源码学习(ArrayList,CopyOnWriteArrayList)

ArrayList:基于动态数组实现,访问较快,增删较慢,线程不安全 实现原理: ArrayList底层实现原理: https://www.cnblogs.com/maoyali/p/8805975.html https://www.cnblogs.com/AmyZheng/p/9427140.html 构造函数: 无参构造: public ArrayList() { //对象数组就指向到一个空数组 private static final Object[] DEFAULTCAPACITY_E

Fuly的博客 179

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

学习Java中的队列:Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList

1、Vector 背景:Vector是在 JDK 1.0 提供的,但是事实上已经很少有人使用了。主要原因是性能差,且不符合大部分开发时的需求。但是在 JDK 中,Vector一直没有被标记为Deprecated。 实现:Vector是基于数组实现,几乎在所有操作方法上,都用synchronized关键字实现方法同步,这种同步方式可以对单一操作进行加锁,比如多个线程同时执行add会同步阻塞执行,但是多线程执行add和remove时,就不会阻塞了。 缺点:大部分需要对队列加锁的场景,是想对整个队列加锁,而不仅仅

LazyCancerPatiens的博客 603

java并发下修改arrayList---CopyOnWriteArrayList

背景:我将配置文件保存在ArrayList中, 多线程并发读取配置.但是我每5分钟 会去扫描一下配置文件,如果修改了配置文件 就会重新加载新的配置文件. 但在多线程共享同一个资源下修改就会抛出Java.util.ConcurrentModificationException 错误.这里有两种解决方法: 第一种方法是:加锁 缺点:性能差   优点:保证数据的实时一致性   修改后就可以让接下来

进击的小白 1391

看山聊并发:认识 Java 中的队列:Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList

你好,我是看山。 书接上文,上次聊了聊 在多线程中使用 ArrayList 会发生什么,这次我们说说平时常用的列表:Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList。 Vector Vector是在 JDK 1.0 提供的,虽然没有被标记Deprecated,但是事实上已经没人使用了。主要原因是性能差,且不符合需求。 从源码可以看出(这里不贴源码了),Vector是基于数组实现,几乎在所有操作方法上,都用synchronized关键字实现方法同.

沉潜飞动 1431

java arraylist遍历删除_ArrayList和CopyOnWriteArrayList的遍历与删除

ArrayList的遍历方法与删除中的异常情况分析package com.test;import java.util.ArrayList;import java.util.Iterator;import java.util.List;/*** @author yuhao**/public class Test {private static List list = new ArrayList&lt...

weixin_29768055的博客 785

java中怎么给方法加锁_Java中,我会用ArrayList,怎么还要会用CopyOnWriteArrayList

前言之前的文章已经说过了java开发中,保存集合数据是用ArrayList还是LinkedList,了解下:Java编程中我该用ArrayList还是LinkedList?平常面试过程中问的最多的就是 HashMap,当问到可以保证线程安全的并发容器时,最熟悉的就是ConcurrentHashMap,也了解下:我已经学了HashMap,怎么还要会用ConcurrentHashMap但是如果问 “有...

weixin_42122838的博客 633

学习笔记(三):Java中的List集合——ArrayList、LinkedList、Vector、Stack、CopyOnWriteArrayList

List集合详解以及知识点汇总

weixin_52279910的博客 640

Java ArrayList线程安全实战:从synchronized到CopyOnWriteArrayList

在Java并发编程中,集合的线程安全是保障系统稳定性的核心基础。ArrayList作为最常用的动态数组实现,其非线程安全的特性源于内部状态在多线程下的读写缺乏同步,这涉及到Java内存模型(JMM)的可见性与原子性问题。为解决高并发场景下的数据竞争,开发者需掌握外部同步锁、写时复制等关键技术。其中,synchronized通过互斥锁提供强一致性,适用于并发度不高的场景;而CopyOnWriteArrayList采用读写分离的副本机制,在读多写少的配置缓存、事件监听器等场景中能提供极高的读并发性能。合理选择线

weixin_34163553的博客 334

【重难点】【Java集合 03】ArrayList、LinkedList、 Vector 和 Stack 的区别、CopyOnWriteArrayList

ArrayList、LinkedList 和 Vector 的区别、util 包下的 List、CopyOnWriteArrayList

2022年毕业的萌新程序猿,输出一下自己的学习内容~ 420

【JAVA集合详解】Vector与ArrayList与CopyOnWriteArrayList区别

1. Vector & ArrayList  1)  Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。  2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%

Chenny的blog 950

java vector list_认识 Java 中的队列:Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList...

你好,我是看山。书接上文,上次聊了聊 在多线程中使用 ArrayList 会发生什么,这次我们说说平时常用的列表:Vector、ArrayList、CopyOnWriteArrayList、SynchronizedList。VectorVector是在 JDK 1.0 提供的,虽然没有被标记Deprecated,但是事实上已经没人使用了。主要原因是性能差,且不符合需求。从源码可以看出(这里不贴源码...

weixin_42499335的博客 158
上一篇: Ubuntu中使用virtualenv安装tensorflow
下一篇: Application Loader上传成功了 iTunes Connect不显示构建版本
Pncoder
博客等级 码龄15年 17粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值