面试官让我写一个 ArrayList 的线程不安全的“bug”,并修复它!

我们都知道,ArrayList 天生就不是线程安全的,但是很多人也就是道听途说而已。并没有实际的进行测试,也并不清楚 ArrayList 为什么不是线程安全的!

这不,昨天还有人问我为什么?说面试官让他写一个 bug。答案其实很简单,今天我们就一起来动手写一个“bug”!

面试官让我写一个 ArrayList 的线程不安全的“bug”,并修复它!
写一个 ArrayList 的线程不安全的“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”,并修复它!

: » 面试官让我写一个 ArrayList 的线程不安全的“bug”,并修复它!

原创文章,作者:sunnyman218,如若转载,请注明出处:https://blog.ytso.com/252123.html

(0)
上一篇 2022年5月4日
下一篇 2022年5月4日

相关推荐

发表回复

登录后才能评论