原因
因为他的读写方法没有同步策略,会导致脏数据和不可预期的结果。
// 非原子方法,并且没有同步策略
public class ArrayList<E>
{
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
}
复现
package com.wenxiaowu.concurrent;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 复现ArrayList线程不安全问题
*/
public class ArrayList1Test {
public static void main(String[] args) {
final List<String> arrayList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(() -> {
Thread currentThread = Thread.currentThread();
String threadName = currentThread.getName();
for (int j = 0; j < 5; j++) {
String nowTime = getNowTime();
arrayList.add(String.format("%d", j));
System.out.println(String.format("%s %s, list size:%d, detail:%s",
nowTime, threadName, arrayList.size(), arrayList.toString()));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
});
thread.setName(String.format("thread%d", i));
thread.start();
}
}
private static String getNowTime() {
Date day = new Date();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return df.format(day);
}
}
出现了经典的并发修改问题
注:有的时候需要多试几次才能复现。
解决办法
final List<String> arrayList = new ArrayList<>(); 改成 final List<String> arrayList = new CopyOnWriteArrayList<String>(); 即可
底层解析:CopyOnWriteArrayList存储数据的数组是volatile修饰的,线程可见,禁止重排。在高并发场景里面,重要的核心变量都需要加volatile。