记一次ArrayList多线程使用时ArrayIndexOutOfBoundsException异常问题处理

timo-nbktp 1年前 ⋅ 575 阅读

今天收到业务方反馈,调用接口有异常发生,而且随着流量增大,异常也增多了。小白赶紧查看监控日志,发现ArrayIndexOutOfBoundsException数组越界异常变多了。于是开始进行排查。

排查时通过回放有问题的请求参数,生产环境的接口有时会报异常,有时是正常的,明明请求参数是一样的,为什么结果会不同呢。小白猜测可能是多线程使用的问题

先来看看系统逻辑
 

再来看看相关代码,这里小白用了模拟的方式写多线程那块的代码,并非真实业务代码

List<Integer> idList = Lists.newArrayList();
        for (int i = 0; i < 100000; i++) {
            idList.add(i);
        }

        List<Integer> result = Lists.newArrayList();
				//多线程方式进行id操作,并把结果都放到result中
        idList.parallelStream().forEach(id -> {
            result.add(id);
        });
        System.out.println(result.size());

运行这段代码,就会出现ArrayIndexOutOfBoundsException异常

问题解决

通过查看代码可以发现result变量是ArrayList类型,非线程安全的,多个线程同时往ArrayList里插入数据就会发生多线程问题。解决办法也很简单,对result变量加锁,每次插入数据时都需要获取锁,就能解决并发问题了。

idList.parallelStream().forEach(id -> {
    //对result变量加锁
    synchronized (result) {
        result.add(id);
    }
});

注意:以上方式虽然保证了数据的一致性,但是并行流的效率可能会受到影响,因为多个线程需要等待锁的释放才能执行操作。可以使用并发安全的容器类ConcurrentLinkedQueue,避免多线程操作List时出现的线程安全问题。同时,使用Stream的collect方法将结果收集到ConcurrentLinkedQueue中。

代码优化如下:

ConcurrentLinkedQueue result = idList.parallelStream() .collect(Collectors.toConcurrentLinkedQueue());

问题分析

问题解决了,但是为什么报的错误是数组越界异常呢?我们知道ArrayList是有自动扩容机制的,数组放不下了,会自动扩容的,不应该会报异常啊。带着疑问,小白又翻了翻ArrayList的源代码查看

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //这儿就是关键点,两个线程先后到达这儿,检查都发现不需要扩容,第一个线程在执行下面的步骤的时候没有问题,第二个就会数组越界了。
        elementData[size++] = e;
        return true;
    }

通过上面的代码可以看出,两个线程在add元素时,会执行ensureCapacityInternal方法判断是否扩容。判断都不需要扩容,但数组就剩一个空位时,第一个线程执行了添加操作没有问题。此时第二个线程再执行添加操作就会发生数组越界了

小白分析完问题后,不得不感叹,没想到经常用的ArrayList也有自己不知道的问题。不过也正因为有这次踩坑经历,小白对并发编程的了解又多了一些。
 

 

-------end-----------

 

 

版权 本着开源共享、共同学习的精神,本文转载自 https://blog.csdn.net/qq_36624086/article/details/128607367 , 如果侵权之处,请联系博主进行删除,谢谢~