【List篇】ArrayList 的线程不安全介绍

ArrayList 为什么线程不安全?

主要原因是ArrayList是非同步的,没有同步机制,并且其底层实现是基于数组,而数组的长度是固定的。当对 ArrayList 进行增删操作时,需要改变数组的长度,这就会导致多个线程可能同时操作同一个数组,从而引发线程安全问题。
具体来说,如果多个线程同时对 ArrayList 进行写操作(add、remove 等),可能会导致以下问题:

  • 数据不一致:多个线程同时修改 ArrayList 的元素,可能会导致数据不一致的情况。例如,一个线程正在修改一个元素,而另一个线程正在读取该元素,这时就会出现数据不一致的情况。

  • 索引越界:如果多个线程同时进行添加或删除元素操作,就可能导致索引越界的情况。例如,一个线程正在删除 ArrayList 中最后一个元素,而另一个线程正在向 ArrayList 中添加元素,这时就可能导致索引越界的情况。

/*** 模拟ArrayList线程不安全*/
public class UnsafeArrayList {public static void main(String[] args) {List<Integer> list = new ArrayList<>();//线程1,添加1000个元素Thread t1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {list.add(i);}});//线程2,添加1000个元素Thread t2 = new Thread(() -> {for (int i = 1000; i < 2000; i++) {list.add(i);}});//启动线程t1.start();t2.start();try {//等待两个线程执行完毕t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("期望size=2000,实际size=" + list.size());}
}

上面的代码中,我们创建了两个线程 t1 和 t2,分别向 ArrayList 中添加了 1000 个元素。但是,由于 ArrayList 不是线程安全的,所以在多线程环境下,两个线程可能会同时访问 ArrayList,导致线程安全问题。
运行上面的代码,结果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
输出结果可能不一定是 2000,可能会小于 2000,这是因为线程之间的操作是交错执行的,有可能一个线程正在添加元素时,另一个线程就已经读取了数组的大小并返回了结果。这样就会导致对数组的修改操作在并发情况下会导致线程竞争和不确定性的结果。

  • ConcurrentModificationException场景 : 这个异常发生的原因是在迭代集合时,有一个线程对集合进行了修改,并且另一个线程正在运行迭代器,这时就会发生ConcurrentModificationException异常。

为了模拟这个异常,我们可以使用Java的多线程功能。下面是一个简单的示例代码,可以模拟ConcurrentModificationException异常:

public class ConcurrentModificationDemo {public static void main(String[] args) {//模拟在多线程场景下,由于ArrayList线程不安全引发的ConcurrentModificationException异常场景List<Integer> list = new ArrayList<>();list.add(1000);list.add(2000);list.add(3000);//执行迭代的逻辑Runnable task = () -> {Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String fruit = iterator.next();System.out.println(fruit);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}};//线程1执行迭代逻辑Thread thread1 = new Thread(task);//线程2 插入元素Thread thread2 = new Thread(() -> list.add(4000));thread1.start();thread2.start();}
}

在这个示例代码中,我们创建了一个包含三个字符串元素的列表,并使用两个线程来模拟ConcurrentModificationException异常。
一个线程使用列表的迭代器来遍历列表,并在每个元素输出之后休眠1秒。另一个线程在1秒后向列表添加一个新元素。
当第二个线程添加一个新元素时,它会修改列表,而第一个线程仍在使用迭代器遍历列表。因此,当第一个线程尝试访问下一个元素时,就会抛出ConcurrentModificationException异常。
注意,由于多线程环境的不确定性,实际运行结果可能有所不同。
在这里插入图片描述

如何解决ArrayList的线程不安全?

1.使用Collections.synchronizedList方法将ArrayList转换成线程安全的List。该方法返回一个线程安全的List,它会在每个方法调用时同步访问

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

2.使用CopyOnWriteArrayList类来替代ArrayList。CopyOnWriteArrayList是Java并发包中提供的一个线程安全的List,它通过使用写时复制技术(Copy-On-Write)来实现线程安全,每次写操作都会创建一个新的数组,读操作不会加锁,因此性能较高。

List<Integer> copyOnWriteList = new CopyOnWriteArrayList<Integer>();

3.使用Locksynchronized关键字来保证多个线程对ArrayList的操作同步。可以通过在访问ArrayList的代码块上添加synchronized关键字,或者使用Java并发包中的Lock类来实现同步。但是需要注意,这种方式可能会出现死锁等问题,并且性能也会受到影响。

private static Lock lock = new ReentrantLock();
private static List<Integer> list = new ArrayList<>();public static void addToArrayList(Integer element) {//加锁lock.lock();try {list.add(element);} finally {//释放锁lock.unlock();}
}
  1. 使用线程安全的并发容器,如ConcurrentHashMap等。

ArrayList既然线程不安全 为什么还要使用?

虽然ArrayList是线程不安全的,但是它具有以下优点:

  • 简单易用:ArrayList是Java集合框架中最简单且最常用的类之一。
  • 高效性能:相比于线程安全的Vector类,ArrayList在单线程环境下具有更高的性能。
  • 空间利用率高:ArrayList在内存使用上比数组更加灵活,能够优化内存利用率。
  • 可扩展性强:ArrayList自带自动扩容机制,能够自动调整数组大小,支持动态添加元素。

因此,对于单线程环境下的数据存储和访问,使用ArrayList是一种非常方便和高效的选择。

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

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

相关文章

222. 完全二叉树的节点个数

题目链接&#xff1a; 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 我的想法&#xff1a; 递归法 万金油--层次遍历法 当然上面两中都是笨方法&#xff0c;就算不是完全二叉树也能算&#xff0c;没有用到完全二叉树的特性。 我的代码&#xff1…

【个人博客系统网站】注册与登录 · 加盐加密验密算法 · 上传头像

【JavaEE】进阶 个人博客系统&#xff08;3&#xff09; 文章目录 【JavaEE】进阶 个人博客系统&#xff08;3&#xff09;1. 加盐加密验密算法原理1.1 md5加密1.2 md5验密1.3 md5缺漏1.4 加盐加密1.5 后端的盐值拼接约定1.6 代码实现1.6.1 加密1.6.2 验密1.6.3 测试 2. 博客…

MySQL的常用术语

目录 1.关系 2.元组 3.属性 MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 1.关系 前面的博客有说到,MySQL是一款关系型数据库管理软件,一个关系就是 一张二维表(表) 我想大家都知道表格怎么…

sqli-labs闯关

目录 less-01: less-08: less-19: less-20: 项目地址—Github 使用HackBar插件 less-01: Sqli-labs前20关均为数字型注入 Sqli-labs前四关较为类似以less-01为模板 将网址导入HackBar中&#xff1a; 1.根据提示&#xff0c;输入http://127.0.0.1/sqli/Less-1/?id1查看…

算法[动态规划]---买卖股票最佳时机

1、题目&#xff1a; 给你一个整数数组 prices&#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候最多只能持一股股票。你也可以先购买&#xff0c;然后在同一天出售。 返回你能获得的最大利润 。 2…

9. xaml ComboBox控件

1.运行图像 2.运行源码 a.Xaml源码 <Grid Name="Grid1"><!--IsDropDownOpen="True" 默认就是打开的--><ComboBox x:Name="co

完成Centos上使用SSH公钥进行免密上传文件到gitee的步骤后,测试免密推送到gitee的时候还是需要输入邮箱和密码

如果你已经按照正确的步骤设置了SSH公钥并进行了免密测试&#xff0c;但仍然需要输入邮箱地址和密码才能推送到gitee&#xff0c;那么可能有以下几种原因&#xff1a; 您可能没有使用SSH URL来推送代码。请确保您使用的是SSH URL而不是HTTPS URL来推送代码。您可以使用命令 gi…

电商3D资产优化管线的自动化

如果你曾经尝试将从 CAD 程序导出的 3D 模型上传到 WebGL 或 AR 服务&#xff0c;那么可能会遇到最大文件大小、永无休止的进度条和糟糕的帧速率等问题。 为了创作良好的在线交互体验&#xff0c;优化 3D 数据的大小和性能至关重要。 这也有利于你的盈利&#xff0c;因为较小的…

GDB用法(三)

预备 测试代码参照GDB用法(二) 命令历史 可以将命令历史保存到文件中 (show history) 展示当前gdb中history的设置信息 设置expansion (set history expansion) 打开历史扩展 能使用历史处理命令对历史数据进行处理, 暂不细究 (show history expansion) 展示历史扩展配置…

什么是原生IP?原生IP与住宅IP有何区别?

相信许多做跨境的都会接触到IP代理&#xff0c;比如电商平台、社媒平台、收款平台等等&#xff0c;都会检测IP。那也会经常听到一些词汇&#xff1a;原生IP、住宅IP&#xff0c;这两者之间有什么区别呢&#xff1f;什么业务需要用到呢&#xff1f;接下来带大家具体了解一下。 什…

软件架构设计(十三) 构件与中间件技术

中间件的定义 其实中间件是属于构件的一种。是一种独立的系统软件或服务程序,可以帮助分布式应用软件在不同技术之间共享资源。 我们把它定性为一类系统软件,比如我们常说的消息中间件,数据库中间件等等都是中间件的一种体现。一般情况都是给应用系统提供服务,而不是直接…

Jenkins介绍

Jenkins介绍 持续集成、持续部署的工具很多&#xff0c;其中Jenkins是一个开源的持续集成平台。 Jenkins涉及到将编写完毕的代码发布到测试环境和生产环境的任务&#xff0c;并且还涉及到了构建项目等任务。 Jenkins需要大量的插件保证工作&#xff0c;安装成本较高&#xff0…

MyBatis框架中各种参数类型绑定的方式

MyBatis框架中各种参数类型绑定的方式 一、MyBatis参数绑定 MyBatis框架中&#xff0c;通过Mapper接口和Mapper映射文件的方式来操作数据库的时候&#xff0c;可能需要通过Mapper接口中的方法传递相应的参数拼接到SQL语句上面&#xff0c;那么Mybatis将传递的参数映射到对应S…

Debian11安装MySQL8.0,链接Navicat

图文小白教程 1 下载安装MySQL1.1 从MySQL官网下载安装文件1.2 安装MySQL1.3 登录MySQL 2 配置Navicat远程访问2.1 修改配置2.2 Navicat 连接 end: 卸载 MySQL 记录于2023年9月&#xff0c;Debian11 、 MySQL 8.0.34 1 下载安装MySQL 1.1 从MySQL官网下载安装文件 打开 MySQ…

FFmpeg入门之FFmpeg源码编译

1.源码下载: git clone https://github.com/FFmpeg/FFmpeg.git windows : macos: ubuntu: 2.编译FFmpeg CompilationGuide – FFmpeg windows: 1.下载yasm并安装 : Download - The Yasm Modular Assembler Project 下载后复制到c:/windows 2.下载SDL 3.下载H264/265源码 git…

stringBuffer.append(“字符串参数“);这个在字符串参数后添加空格怎么写

stringBuffer.append(“字符串参数”);这个在字符串参数后添加空格怎么写&#xff1f; 要在字符串参数后添加空格&#xff0c;可以直接在字符串参数的末尾使用空格字符&#xff0c;像这样&#xff1a; stringBuffer.append("字符串参数 ");这样就在字符串参数后添加…

【HTML5高级第三篇】drag拖拽、音频视频、defer/async属性、dialog应用

文章目录 一、拖拽事件1.1 拖拽事件1.2 案例&#xff1a;拖拽丢弃图片 二、音频和视频三、defer 与 async 属性3.1 概述3.2 示例一&#xff1a;3.3 示例二&#xff1a; 四、dialog 元素 一、拖拽事件 原生JavaScipt案例合集 JavaScript DOM基础 JavaScript 基础到高级 Canvas…

637. 二叉树的层平均值

637. 二叉树的层平均值 题目-简单难度示例1. bfs 题目-简单难度 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 示例 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&…

爬虫逆向实战(29)-某蜂窝详情页(cookie、混淆、MD5、SHA)

一、数据接口分析 主页地址&#xff1a;某蜂窝 1、抓包 通过抓包可以发现数据是静态的&#xff0c;在html中。 2、判断是否有加密参数 请求参数是否加密&#xff1f; 无请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cookie是否加密&#xff1f; 通过查看“c…

Windows wsl2安装Ubuntu

wsl&#xff08;Windows Subsystem for Linux&#xff09;即适用于Windows的Linux子系统&#xff0c;是一个实现在Windows 10 / 11上运行原生Linux的技术。 wsl2 为其迭代版本&#xff0c;可以更好的在Windows上运行Linux子系统。 这里以 Windows 11 安装Ubuntu作为示例。 开启…