当前位置:网站首页>如果非要在多线程中使用 ArrayList 会发生什么?
如果非要在多线程中使用 ArrayList 会发生什么?
2022-07-29 01:47:00 【JavaMonsterr】
为了便于理解,当时只是通过代码执行顺序说明了异常原因。其实多线程中还会涉及 Java 内存模型,本文就从这方面说明一下。
对比源码
我们先来看看 Java 11 中, add 方法做了什么调整。
Java 8 中 add 方法的实现:
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
Java 11 中 add 方法的实现:
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}两段逻辑的差异在于数组下标是否确定:
elementData[size++] = e;,Java 8 中直接使用size定位并赋值,然后通过size++自增elementData[s] = e; size = s + 1;,Java 11 借助临时变量s定位并赋值,然后通过size = s + 1给size赋新值
Java 11 的优点在于,为数组指定元素赋值的时候,下标值是确定的。也就是说,只要进入 add(E e, Object[] elementData, int s) 方法中,就只会处理指定位置的数组元素。并且, size 的值也是根据 s 增加。按照执行顺序推断,最终的结果可能会丢数,但是不会出现 null。(多个线程向同一个下标赋值,即 s 相等,那最终 size 也相等。)
验证一下
让我们来验证下。
package com.kuaishou.is.datamart;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch waiting = new CountDownLatch(3);
Thread t1 = new Thread(() -> {
try {
latch.await();
for (int i = 0; i < 1000; i++) {
list.add("1");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
waiting.countDown();
}
});
Thread t2 = new Thread(() -> {
try {
latch.await();
for (int i = 0; i < 1000; i++) {
list.add("2");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
waiting.countDown();
}
});
Thread t2 = new Thread(() -> {
try {
latch.await();
for (int i = 0; i < 1000; i++) {
list.add("2");
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
waiting.countDown();
}
});
t1.start();
t2.start();
latch.countDown();
waiting.await();
System.out.println(list);
}
}在 Java 8 和 Java 11 中分别执行,果然,出现了 ArrayIndexOutOfBoundsException 和 null 的情况。如果没有出现,那就是姿势不对,需要多试几次或者多几个线程。
换个角度想问题
上一篇通过代码执行顺序解释了出现问题的原因,这次再看看 JMM 的原因。

从上图我们可以看到,Java 为每个线程创建了一个本地内存区域,也就是说,代码运行过程中使用的数据,是线程本地缓存的数据。这份缓存的数据,会与主内存的数据做交换(更新主内存数据或更新本次缓存中的数据)。
我们通过一个时序图看下为什么会出现 null(数组越界异常同理):

从时序图我们可以看出现,在执行过程中,两个线程取的 size 值和 elementData 数组地址,大部分是操作自己本地缓存中的,执行一段时间后,会将本地缓存中的数据写回主内存数据,然后还会从主内存中读取最新数据更新本地缓存数据。异常就在这个交换过程中发生了。
这个时候,可能有读者会想,是不是把 size 和 elementData 两个变量加上 volatile 就可以解决了。如果这样想,那你就想简单。线程安全是整个类设计实现时已经确定了,除了属性需要考虑多线程的影响,方法(主要是会修改属性元素的方法)也需要考虑。
ArrayList 的定位是非线程安全的,其中的所有方法都没有考虑多线程下为共享资源加锁。即使 size 和 elementData 两个变量都是实时读写主内存,但是 add 和 grow 方法还是可能会覆盖另一个线程的数据。
我们从 ArrayList 的 add 方法注释可以得知,方法拆分不是为了实现线程安全,而是为了执行效率和内存占用:
This helper method split out from add(E) to keep method bytecode size under 35 (the -XX:MaxInlineSize default value), which helps when add(E) is called in a C1-compiled loop.
所以说,在多线程场景下使用 ArrayList ,该出现的异常,一个也不会少。
边栏推荐
- The problem of modifying the coordinate system of point cloud image loaded by ndtmatching function in autoware
- QT source code analysis -- QObject (4)
- 基于对象的实时空间音频渲染丨Dev for Dev 专栏
- 第3章业务功能开发(线索备注的删除和修改)
- Internet of things development -- mqtt message server emqx
- The financing demand of 129 million yuan was released, and the roadshow of the Dake city project continued to irrigate the "good seedlings" of scientific innovation
- Navigation--实现Fragment之间数据传递和数据共享
- 响应式织梦模板化妆美妆类网站
- Complete collection of common error handling in MySQL installation
- Form verification hidden input box is displayed before verification
猜你喜欢

Prometheus + AlertManager 消息预警

Rgbd point cloud down sampling

Navigation -- realize data transmission and data sharing between fragments
[email protected] The localization rate reaches 100%"/>Quanzhi t3/a40i industrial core board, 4-core [email protected] The localization rate reaches 100%

基于C51控制蜂鸣器
![[circuit design] peak voltage and surge current](/img/d5/45bf9a79171ff9b8d7ba4c771b340c.png)
[circuit design] peak voltage and surge current

第3章业务功能开发(线索备注的删除和修改)

发布融资需求1.29亿元,大科城项目路演持续浇灌科创“好苗子”

当我看源码的时候,我在想什么?
![[cloud native] what is the microservice architecture](/img/84/a0ec68646083f3539aa39ad9d98749.png)
[cloud native] what is the microservice architecture
随机推荐
基于对象的实时空间音频渲染丨Dev for Dev 专栏
Prometheus + AlertManager 消息预警
「活动推荐」冲冲冲!2022 国际开源节有新内容
Full solution of 3D model format | including more than 70 kinds of RVT, 3ds, DWG, FBX, IFC, osgb, obj, etc
多线程浅谈
Day 15 (VLAN related knowledge)
Derivation of Euler angle differential equation
Navigation -- realize data transmission and data sharing between fragments
Keil5 open the engineering prompt not found device solution
网络安全漏洞管理的探索与实践
Basic working principle and LTSpice simulation of 6T SRAM
【上传图片可剪裁-1】
QT memory management tips
"Wei Lai Cup" 2022 Niuke summer multi school training camp 2, sign in question GJK
Data security and privacy computing summit - development and application of security intersection in privacy Computing: Learning
第十五天(VLAN相关知识)
Motionlayout -- realize animation in visual editor
费曼学习法(符号表)
Read the recent trends of okaleido tiger and tap the value and potential behind it
Related function records about string processing (long-term update)