ArrayList 多线程报 数组越界异常

2019-01-22  本文已影响0人  半支铅笔半块橡皮

使用ArrayList在多线程环境可能会出现ArrayIndexOutOfBoundsException 异常,这篇文章就来看看为什么会出现这个错误。
先看看下面的实例:

public class ArrayListConcurrent {

    private static List<Integer> numberList = new ArrayList<>(100000);

    public static class AddToArrayList extends Thread{
        int startNum = 0;

        public AddToArrayList(int startNum){
            this.startNum = startNum;
        }

        @Override
        public void run() {
            int count = 0;
            while (count < 1000000){
                numberList.add(startNum);
                startNum += 2;
                count++;
            }
        }
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(new AddToArrayList(0));
        Thread thread2 = new Thread(new AddToArrayList(1));
        thread1.start();
        thread2.start();
    }
}

在运行时报了如下的错误:

Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 174718
    at java.util.ArrayList.add(ArrayList.java:412)
    at com.yumoon.concurrent.ArrayListConcurrent$AddToArrayList.run(ArrayListConcurrent.java:24)
    at java.lang.Thread.run(Thread.java:722)

下面是几组测试数据:


测试数据.png

我们打开ArrayList的412这一行,看看源码:

public boolean add(E e) {
  ensureCapacityInternal(size + 1);  // Increments modCount!!
  //这儿就是分行符,两个线程先后到达这儿,检查都发现不需要扩容,第一个线程在执行下面的步骤的时候没有问题,第二个就有数组越界了。
  elementData[size++] = e;      //这儿是412
  return true;
}

结合源码分析,看看ensureCapacityInternal 方法:

//这个方法就是用来判断是否需要扩容的方法
//结合前面的代码就是说只有有一个位置都可以
private void ensureCapacityInternal(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

比如 22 这儿报错就是因为 10 + 5 + (10+5)>> 1 = 22, (PS:需要熟悉ArrayList的扩容操作)
总结:在多线程同时觉得不需要扩容,但是实际已经到了扩容的临界值的时候,就爆发了这个异常。

上一篇下一篇

猜你喜欢

热点阅读