当前位置:网站首页>如果非要在多线程中使用 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 ,该出现的异常,一个也不会少。
边栏推荐
- Click back to the top JS
- Resnet50 + k-fold cross validation + data enhancement + drawing (accuracy, recall, F value)
- C language improvement (I)
- Even PostgreSQL problem: expected authentication request from server, but received V
- Cookies and sessions
- 多线程浅谈
- 连PostgreSQL问题:expected authentication request from server, but received v
- Try to understand the essence of low code platform design from another angle
- Sharpness evaluation method without reference image
- 什么是作用域和作用域链
猜你喜欢

C语言提高篇(一)

Object based real-time spatial audio rendering - Dev for dev column
![[one · data | chained binary tree]](/img/83/d62a47f1264673f1e898335303a7a6.png)
[one · data | chained binary tree]

Detailed explanation of IVX low code platform series -- Overview (II)

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

QT qstackedwidget multi interface switching

WebView attack

什么是作用域和作用域链

autoware中ndtmatching功能加载点云图坐标系修正的问题

"Wei Lai Cup" 2022 Niuke summer multi school training camp 2, sign in question GJK
随机推荐
[circuit design] open collector OC output of triode
Responsive dream weaving template hotel room website
WebView attack
Three methods of STM32 delay function
开启TLS加密的Proftpd安全FTP服务器安装指南
Character flow comprehensive exercise problem solving process
物联网开发--MQTT消息服务器EMQX
PS + PL heterogeneous multicore case development manual for Ti C6000 tms320c6678 DSP + zynq-7045 (2)
字符流综合练习解题过程
【MQTT从入门到提高系列 | 09】WireShark抓包分析MQTT报文
Motionlayout -- realize animation in visual editor
实验二:Arduino的三色灯实验
What is scope and scope chain
mobile-picker.js
应用系统中的报表开发成本值多少?
Understand the clock tree in STM32 in simple terms
[circuit design] peak voltage and surge current
多线程浅谈
第十五天(VLAN相关知识)
响应式织梦模板化妆美妆类网站