为什么ArrayList是线程不安全的?

2021-01-26  本文已影响0人  nitricoxide

先看看多个线程同时对一个ArrayList进行add的操作

public static void main(String[] args) throws InterruptedException {
    ArrayList<Integer> list = new ArrayList<>();

    Thread thread0 = new Thread(() -> {
        for (int i = 0; i < 100; i++) {
            list.add(i);
        }
    });

    Thread thread1 = new Thread(() -> {
        for (int i = 0; i < 100; i++) {
            list.add(i);
        }
    });

    thread0.start();
    thread1.start();

    Thread.sleep(100);

    System.out.println(list.size());

}

结果:

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 10
    at java.util.ArrayList.add(ArrayList.java:459)
    at com.ldy.demoz.delete.Test.lambda$main$0(Test.java:21)
    at java.lang.Thread.run(Thread.java:745)
101

看一下报错行源码:


原因

  1. 列表大小为9,即size=9
  2. 线程0开始进入add方法,这时它获取到size的值为9,调用ensureCapacityInternal方法进行容量判断。
  3. 线程1此时也进入add方法,它获取到size的值也为9,也开始调用ensureCapacityInternal方法。
  4. 线程0发现需求大小为10,而elementData的大小就为10,可以容纳。于是它不再扩容,返回。
  5. 线程1也发现需求大小为10,也可以容纳,返回。
  6. 线程0开始进行设置值操作, elementData[size++] = e 操作。此时size变为10。
  7. 线程1也开始进行设置值操作,它尝试设置elementData[10] = e,而elementData没有进行过扩容,它的下标最大为9。于是此时会报出一个数组越界的异常ArrayIndexOutOfBoundsException.
    (具体源码可参考ArrayList源码分析

一句话来说就是ArrayList的操作不是原子性的。

上一篇 下一篇

猜你喜欢

热点阅读