美文网首页
ArrayList多线程调用add()可能出现的问题

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

作者: 花神子 | 来源:发表于2019-09-16 14:18 被阅读0次

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

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

    下标越界问题 java.lang.ArrayIndexOutOfBoundsException

    public class ArrayListTest {
    
        public static List<Integer> 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()可能出现的问题

          本文链接:https://www.haomeiwen.com/subject/bhoxuctx.html