ArrayList多线程调用add()可能出现的问题

ArrayList多线程调用add()可能出现的问题

一 ArrayList在多线程调用Add()添加元素时的下标越界问题

下标越界问题 java.lang.ArrayIndexOutOfBoundsException

public class ArrayListTest {

    public static List list = new ArrayList<>();

    static class AddToList implements Runnable {
        int num = 0;

        public AddToList(int num) {
            this.num = num;
        }

        @Override
        public void run() {
            int count = 0;
            while (count < 100) {
                list.add(num);
                num += 1;
                count++;
            }
        }
    }

    public static void main(String[] args) {
        new Thread(new AddToList(0)).start();
        new Thread(new AddToList(1)).start();
    }
}

同时使用两个线程进行添加数据,很容易会出现如下异常:

Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 73
    at java.util.ArrayList.add(ArrayList.java:459)
    at com.algo.hongbao.ArrayListTest$AddToList.run(ArrayListTest.java:21)
    at java.lang.Thread.run(Thread.java:748)

ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长查看ArrayList.add(E e) 方法的添加流程:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
} 

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}



  • 首先会调用ensureCapacityInternal(size + 1),其作用为保证数组的容量始终够用,其中size是elementData数组中元组的个数,初始为0。

  • ensureCapacityInternal()函数中,用if判断,如果数组没有元素,给数组一个默认大小,会选择实例化时的值与默认大小中较大值,然后调用ensureExplicitCapacity

  • 函数体中,modCount是数组发生size更改的次数。然后if判断,如果数组长度小于默认的容量10,则调用扩大数组大小的方法grow()。

  • 函数grow()解释了基于数组的ArrayList是如何扩容的。数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。

  • 回到Add()函数,继续执行,elementData[size++] = e;

根据上面执行流程,其实我们很容器发现问题的所在,其实就是最后这一行代码elementData[size++] = e;导致。添加一个元素我们可以理解为:

  1. 在 elementData[Size] 的位置存放此元素;
  2. 增大 Size 的值。

用第一次下标为15时的异常举例。当集合中已经添加了14个元素时,一个线程率先进入add()方法,在执行ensureCapacityInternal(size + 1)时,发现还可以添加一个元素,故数组没有扩容,但随后该线程被阻塞在此处。接着另一线程进入add()方法,执行ensureCapacityInternal(size + 1),由于前一个线程并没有添加元素,故size依然为14,依然不需要扩容,所以该线程就开始添加元素,使得size,变为15,数组已经满了。而刚刚阻塞在elementData[size] = e;语句之前的线程开始执行,它需要在集合中添加第16个元素,而数组容量只有15个,所以就发生了数组下标越界异常!

多线程并发导致List的add()失败,元素为null

执行add方法时,会先将此容器的大小增加。。即size++,然后将传进的元素赋值给新增的elementData[size++],即新的内存空间

但是此时如果在size++后直接来取这个List,而没有让add完成赋值操作,则会导致此List的长度加一,,但是最后一个元素是空(null),所以在获取它进行计算的时候报了空指针异常。

你可能感兴趣的:(ArrayList多线程调用add()可能出现的问题)