当前位置:网站首页>如果非要在多线程中使用 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 ,该出现的异常,一个也不会少。
边栏推荐
- Hexadecimal to string
- H5 background music is played automatically by touch
- 【RT学习笔记1】RT-Thread外设例程——控制Led灯闪烁
- STM32 DMA receives serial port data
- 即时通讯场景下安全合规的实践和经验
- 如何在多御安全浏览器中自定义新标签页?
- 发布融资需求1.29亿元,大科城项目路演持续浇灌科创“好苗子”
- MotionLayout--在可视化编辑器中实现动画
- Derivation of Euler angle differential equation
- Ignore wechat font settings
猜你喜欢

"Activity recommendation" rush rush! 2022 international open source Festival has new content

指针——黄金阶段

【RT学习笔记1】RT-Thread外设例程——控制Led灯闪烁

聊聊接口性能优化的11个小技巧

Understand the working principle of timer in STM32 in simple terms

【ONE·Data || 数组堆简单实现及其延伸】

Object based real-time spatial audio rendering - Dev for dev column

年中总结 | 与自己对话,活在当下,每走一步都算数

Flexible layout single selection

Motionlayout -- realize animation in visual editor
随机推荐
The first of the five tips for using browsers efficiently is the most practical
裂开了,一次连接池参数导致的雪崩问题
Keil5 open the engineering prompt not found device solution
[simple implementation and extension of one · data | array heap]
Excel 用countif 统计
Type analysis of demultiplexer (demultiplexer)
Read the recent trends of okaleido tiger and tap the value and potential behind it
12. < tag dynamic programming and subsequence, subarray> lt.72. edit distance
Day 14: continued day 13 label related knowledge
MySQL stores JSON format data
In 2022, the official data of programming language ranking came, which was an eye opener
Jmeter之BeanShell生成MD5加密数据写入数据库
响应式织梦模板家装装饰类网站
Website Collection
3D模型格式全解|含RVT、3DS、DWG、FBX、IFC、OSGB、OBJ等70余种
[cloud native and 5g] micro services support 5g core network
第十四天:续第十三天标签相关知识
Day 15 (VLAN related knowledge)
基于C51控制蜂鸣器
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