使用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)
下面是几组测试数据:
我们打开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的扩容操作)
总结:在多线程同时觉得不需要扩容,但是实际已经到了扩容的临界值的时候,就爆发了这个异常。