解决 ArrayList 的并发问题

目录

  • 1. 场景复现
    • 1.1 数据不一致问题示例代码
    • 1.2 ConcurrentModificationException 问题示例代码
  • 2. 解决并发的三种方法
    • 2.1 使用 Collections.synchronizedList
    • 2.2 使用 CopyOnWriteArrayList(推荐使用)
    • 2.3 使用显式的同步控制
  • 3. 总结

ArrayListjava.util包中的一个类,它不是线程安全的。如果多个线程同时对同一个ArrayList进行操作,可能会导致并发问题,如数据不一致ConcurrentModificationException异常。

1. 场景复现

1.1 数据不一致问题示例代码

import java.util.ArrayList;
import java.util.List;public class ArrayListConcurrencyExample {public static void main(String[] args) {List<Integer> arrayList = new ArrayList<>();// 创建并启动多个线程,同时向ArrayList添加元素Runnable addTask = () -> {for (int i = 0; i < 1000; i++) {arrayList.add(i);}};Thread thread1 = new Thread(addTask);Thread thread2 = new Thread(addTask);thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}// 输出ArrayList的大小,不一定是预期的 2000 System.out.println("Size of arrayList: " + arrayList.size());}
}

1.2 ConcurrentModificationException 问题示例代码

ConcurrentModificationException通常会在迭代ArrayList(或其他集合)的同时对其进行结构性修改时抛出。

import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;public class ConcurrentModificationExample {public static void main(String[] args) {List<String> arrayList = new ArrayList<>();arrayList.add("Item1");arrayList.add("Item2");arrayList.add("Item3");// 获取迭代器Iterator<String> iterator = arrayList.iterator();// 开始迭代while (iterator.hasNext()) {String item = iterator.next();System.out.println(item);// 在迭代过程中尝试修改ArrayList的结构,会引发ConcurrentModificationExceptionif (item.equals("Item2")) {arrayList.remove(item);}}}
}

当处理ArrayList的并发问题时,不同的方法有不同的细节和适用场景。以下是对每种方法的详细解释:

2. 解决并发的三种方法

2.1 使用 Collections.synchronizedList

使用 Collections.synchronizedList 创建线程安全的ArrayList这是一种简单的方式来使ArrayList线程安全。它实际上是包装了一个原始的ArrayList,并在每个方法上添加synchronized关键字来确保每个方法在同一时间只能由一个线程访问。

List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());

这种方法适用于那些多数情况下是读操作,但偶尔需要写操作的情况。请注意,尽管每个方法都是线程安全的,但多个操作之间没有原子性保证,因此还需要其他方式来确保多个操作的一致性

例如下面的代码就会出现并发问题:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class SynchronizedListExample {public static void main(String[] args) {List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>());Runnable addAndRemoveTask = () -> {for (int i = 0; i < 1000; i++) {synchronizedList.add(i);synchronizedList.remove(synchronizedList.size() - 1);}};Thread thread1 = new Thread(addAndRemoveTask);Thread thread2 = new Thread(addAndRemoveTask);thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Size of synchronizedList: " + synchronizedList.size());}
}

在这个示例中,两个线程同时执行addremove操作。虽然每个操作本身是线程安全的,但它们的组合会导致竞态条件,多次运行后,会出现下面的情况:

  • 最终列表的大小可能不是预期的 2000
  • 由于两个线程同时进行remove操作,可能导致其中一个线程试图删除一个元素,但在另一个线程之前已经删除了,导致IndexOutOfBoundsException异常或其他不一致的结果
    Exception in thread "Thread-0" java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)at java.base/java.util.Objects.checkIndex(Objects.java:372)at java.base/java.util.ArrayList.remove(ArrayList.java:536)at java.base/java.util.Collections$SynchronizedList.remove(Collections.java:2435)at com.test.testlist.SynchronizedListExample.lambda$main$0(SynchronizedListExample.java:14)at java.base/java.lang.Thread.run(Thread.java:834)
    Size of synchronizedList: 1
    

这突显了Collections.synchronizedList在某些情况下可能无法提供足够的并发保护,因此需要额外的同步措施或选择更适合并发操作的数据结构。

2.2 使用 CopyOnWriteArrayList(推荐使用)

CopyOnWriteArrayList是一种并发集合,它通过在写操作时创建一个新的副本来解决并发问题。这意味着读操作不会受到写操作的影响,而且不会抛出ConcurrentModificationException异常。

List<String> list = new CopyOnWriteArrayList<>();

这种方法适用于读操作频繁,写操作较少的情况,因为写操作会比较昂贵。但它非常适用于多线程下的读操作,因为它不需要额外的同步。

2.3 使用显式的同步控制

这种方法需要在需要修改ArrayList的地方使用synchronized块或锁来确保线程安全。这是一种更精细的控制方法,适用于需要更多控制和协同操作的场景。

List<String> list = new ArrayList<>();// 在需要修改list的地方加锁
synchronized (list) {list.add("item");
}

这种方式要求手动管理锁,通过加锁确保在修改ArrayList时进行同步,以防止多个线程同时访问它。

3. 总结

  • 一般在日常编码中,直接使用 CopyOnWriteArrayList 就能满足很多场景;

    但是由于每次进行写操作时,都需要复制整个列表,这会导致写操作的性能较低,尤其在列表很大时。因此,CopyOnWriteArrayList 适用于读操作频繁、写操作较少的场景

  • 使用 CopyOnWriteArrayList 时候,应该避免在迭代过程中修改列表;

    CopyOnWriteArrayList 的迭代器具有弱一致性,在迭代过程中,迭代器可能无法反映出最新的修改,可能会遗漏或重复元素。如果非要强一致性,那就需要全局锁或分布式锁来处理了。

  • 大多数场景中,更多的还是读多写少;

    所以一般解决并发的方法,其实就是让并发写的操作,变成串行的;如果非要保证最终的强一致性,那肯定最终还是串行化处理,非常影响性能。

  • 如果是分布式系统的话,那肯定就要使用分布式锁来处理了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/755009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【JavaWeb】Spring非阻塞通信 - Spring Reactive之WebFlux的使用

【JavaWeb】Spring非阻塞通信 - Spring Reactive之WebFlux的使用 文章目录 【JavaWeb】Spring非阻塞通信 - Spring Reactive之WebFlux的使用参考资料一、初识WebFlux1、什么是函数式编程1&#xff09;面向对象编程思维 VS 函数式编程思维&#xff08;封装、继承和多态描述事物间…

Magical Combat VFX 2

我们为Unity推出的最新资产包:魔法战斗VFX包!这个包非常适合为你的游戏添加激烈而致命的魔法。有30多种独特的效果,包括血液、酸和毒咒,你可以在战斗场景中大显身手。而且移动支持和优化是首要任务,你可以在旅途中使用这些效果,而不用担心性能问题。使用功能齐全、移动就…

【 React 】React render方法的原理?在什么时候会被触发?

1. 原理 首先&#xff0c;render函数在react中有两种形式&#xff1a; 在类组件中&#xff0c;指的是render方法&#xff1a; class Foo extends React.Component {render() {return <h1> Foo </h1>; } }在函数组件中&#xff0c;指的是函数组件本身&#xff1a…

windows11安装SQL server数据库报错等待数据库引擎恢复句柄失败(二)

windows11安装SQL server数据库报错等待数据库引擎恢复句柄失败&#xff08;二&#xff09;&#xff0c;昨天在给网友远程的时候发现了一个新的问题。 计算机系统同样是Windows11&#xff0c;通过命令查出来的扇区相关结果也都是4096&#xff0c;但是最后的安装还是提示SQL ser…

JVM内存模型深度解读

JVM&#xff08;Java Virtual Machine&#xff0c;Java虚拟机&#xff09;对于Java开发者和运行 Java 应用程序而言至关重要。其重要性主要体现在跨平台性、内存管理和垃圾回收、性能优化、安全性和稳定性、故障排查与性能调优等方面。今天就下学习一下 JVM 的内存模型。 一、…

嵌入式学习40-数据结构

数据结构 1.定义 一组用来保存一种或者多种特定关系的 数据的集合&#xff08;组织和存储数据&#xff09; 程序的设计&#xff1a; …

31-Java前端控制器模式(Front Controller Pattern)

Java前端控制器模式 实现范例 前端控制器模式&#xff08;Front Controller Pattern&#xff09;是用来提供一个集中的请求处理机制&#xff0c;所有的请求都将由一个单一的处理程序处理该处理程序可以做认证/授权/记录日志&#xff0c;或者跟踪请求&#xff0c;然后把请求传给…

使用RabbitMQ,关键点总结

文章目录 1.MQ的基本概念2.常见的MQ产品3.MQ 的优势和劣势3.1 优势3.2 劣势 4.RabbitMQ简介4.1RabbitMQ 中的相关概念 1.MQ的基本概念 MQ全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。…

nginx相关内容的安装

nginx的安装 安装依赖 yum install gcc gcc-c automake autoconf libtool make gd gd-devel libxslt-devel -y 安装lua与lua依赖 lua安装步骤如下: mkdir /www mkdir /www/server #选择你自己的目录即可,不需要跟我一致 cd /www/server tar -zxvf lua-5.4.3.tar.gz cd lua-5.4…

掌握FL Studio21的编曲功能,让你的音乐创作更上一层楼

音乐是人类最美好的语言&#xff0c;它能够跨越国界、文化和语言&#xff0c;将人们紧密地联系在一起。在当今数字化时代&#xff0c;音乐创作已经不再是专业人士的专利&#xff0c;越来越多的音乐爱好者开始尝试自己动手制作音乐。而FL Studio21中文版编曲软件正是这样一个为你…

mysql的基本知识点-操作数据库表

创建数据库&#xff1a; CREATE DATABASE database_name;创建一个名字为database_name的数据库&#xff1b; 删除数据库&#xff1a; DROP DATABASE database_name;删除名字为database_name的数据库&#xff1b; 在执行删除数据库操作前&#xff0c;请确保你确实想要删除数据…

pdf文件属性的删除

pdf文件属性的删除 投标过程中需要处理文件属性&#xff0c;特别是word文件属性以及pdf文件的处理 这里讲解pdf文件属性的处理 word处理在我的另外一个博客中&#xff0c;word文件属性的处理 https://ht666666.blog.csdn.net/article/details/134102504 一般用 adobe acroba…

数字人解决方案——ER-NeRF实时对话数字人论文解读

简介 本文提出了一种新的基于条件神经辐射场&#xff08;Condition NeRF&#xff09;的talking portrait合成框架ER-NeRF&#xff0c;能够在较小的参数量下实现高精度的实时渲染和快速收敛。该方法利用空间区域的不平等贡献来指导谈话肖像建模&#xff0c;以提高动态头部重建的…

MNN 围炉札记

文章目录 一、MNN 资料二、使用示例三、源码分析1、createFromFile、createFromBuffer1.1 Content1.2 createFromBufferInternal1.3 Net1.4 Interpreter1.5 Interpreter::Interpreter 2、createRuntime2.1 RuntimeInfo2.2 Schedule::getApprociateType2.2.1 MNNGetExtraRuntime…

《论文阅读》EmpDG:多分辨率交互式移情对话生成 COLING 2020

《论文阅读》EmpDG:多分辨率交互式移情对话生成 COLING 2020 前言简介模型架构共情生成器交互鉴别器损失函数前言 亲身阅读感受分享,细节画图解释,再也不用担心看不懂论文啦~ 无抄袭,无复制,纯手工敲击键盘~ 今天为大家带来的是《EmpDG: Multi-resolution Interactive E…

正则表达式:深入理解与应用

正则表达式&#xff0c;又称正规表示法、常规表示法&#xff08;Regular Expression&#xff0c;在代码中常简写为regex、regexp或RE&#xff09;&#xff0c;是计算机科学中的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。在很多文本编辑器…

Linux 文件系统:文件描述符、管理文件

目录 一、三个标注输入输出流 二、文件描述符fd 1、通过C语言管理文件—理解文件描述符fd 2、文件描述符实现原理 3、文件描述符0、1、2 4、总结 三、如何管理文件 1、打开文件的过程 2、内核空间的结构 struct task_struct&#xff08;PCB&#xff09; struct file…

你真的了解SpringBoot注解?阿里巴巴面试告诉你答案!

如有疑问或者更多的技术分享,欢迎关注我的微信公众号“知其然亦知其所以然”! 大家好,我是小米!今天我来和大家分享一下在阿里巴巴面试中常见的一个问题:SpringBoot注解。SpringBoot作为当今流行的Java开发框架,注解是其灵魂所在,熟练掌握这些注解对于应对面试非常有帮…

腾讯云服务器入站规则端口开放使用指南(CentOS系统)

第一步&#xff1a;开放安全组入站规则 来源处0.0.0.0/0是对IPv4开发&#xff0c;::/0是对IPv6开放&#xff1b; 协议端口按照提示填写即可。云服务器防火墙开放 第三步&#xff1a;本地防火墙开放 sudo firewall-cmd --zonepublic --add-port你的端口号/tcp --perma…

【c++】类和对象

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;c_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.面向过程和面向对象初步认识 面向过程&#xff08;C语言&#xff09; 面向对象&#xff08;C&#xff09; 2.类的引入和定义 2.1 类…