天天看点

java list 多线程add_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;导致。添加一个元素我们可以理解为:

在 elementData[Size] 的位置存放此元素;

增大 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),所以在获取它进行计算的时候报了空指针异常。