我们都知道,ArrayList 天生就不是线程安全的,但是很多人也就是道听途说而已。并没有实际的进行测试,也并不清楚 ArrayList 为什么不是线程安全的!
这不,昨天还有人问我为什么?说面试官让他写一个 bug。答案其实很简单,今天我们就一起来动手写一个“bug”!
看下面这个测试代码:
public class XttblogUnsafeList {
public static void main(String[] args) {
// 进行 10次测试
for (int i = 0; i < 10; i++) {
test();
}
}
public static void test() {
// 用来测试的List
List<Object> list = new ArrayList<Object>();
// 线程数量(100)
int threadCount = 100;
// 用来让主线程等待threadCount个子线程执行完毕
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
// 启动threadCount个子线程
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new MyThread(list, countDownLatch));
thread.start();
}
try {
// 主线程等待所有子线程执行完成,再向下执行
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// List 的size
System.out.println(list.size());
}
}
class MyThread implements Runnable {
private List<Object> list;
private CountDownLatch countDownLatch;
public MyThread(List<Object> list, CountDownLatch countDownLatch) {
this.list = list;
this.countDownLatch = countDownLatch;
}
public void run() {
// 每个线程向List中添加100个元素
for (int i = 0; i < 1000; i++) {
list.add(new Object());
}
// 完成一个子线程(主线程等待子线程执行完了再执行)
countDownLatch.countDown();
}
}
多次执行后,运行结果有两种。一种就是常见的 java.lang.ArrayIndexOutOfBoundsException 异常。
Exception in thread "Thread-100" java.lang.ArrayIndexOutOfBoundsException: 109
at java.util.ArrayList.add(ArrayList.java:463)
at com.xttblog.MyThread.run(XttblogUnsafeList.java:52)
at java.lang.Thread.run(Thread.java:748)
还有一种就是跑成功了,但是数据不对。甚至时不时会抛出个 IndexOutOfBoundsException 异常,这种情况很少见,多数情况下,都是直接抛出异常。
100000
100000
99847
100000
99416
99442
99998
100000
99271
99926
观察上面的代码,其实很简单,就是多线程,向 list 中 add 元素。但是,最终结果却和预期不一致。为什么会漏掉数据呢?
这是因为 List 对象,在做 add 时,执行 Arrays.copyOf 的时候,返回一个新的数组对象。当有线程 t1、t2、… 同时进入 grow方法,多个线程都会执行 Arrays.copyOf 方法,返回多个不同的 elementData 对象,假如,t1 先返回,t2 后返回,那么 List.elementData == t1.elementData,然后 t2 也返回后,这时 List.elementData == t2.elementData,所以,t2.elementData 就把 t1.elementData 数据给覆盖了。导致 t1.elementData 被丢失。
那么这种问题,怎么解决呢?
一种是使用 Vector,还有一种是使用 Collections.synchronizedList(new ArrayList()) 。但是你也要注意符合操作!
这就是,面试中,让我写一个“bug”,并修复一个“bug”。
: » 面试官让我写一个 ArrayList 的线程不安全的“bug”,并修复它!
原创文章,作者:sunnyman218,如若转载,请注明出处:https://blog.ytso.com/252123.html