关闭

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

197人阅读 评论(0) 收藏 举报
分类:

最近在看《实战Java虚拟机》一书,看到有关锁与并发章节时,看到如下一个多线程使用ArrayList的例子:



       两个线程t1和t2同时向numberList中添加数据,由于ArrayList是线程不安全的,因此会导致添加的数据有错误,这个我还是能理解的,但是它报的确是如下错误:


       我就有点理解不了了,ArrayList不是自动扩容、没有长度限制吗,为什么还会出现数组下标越界这种错误呢?

       为了便于分析,我对代码进行了一点点修改:


  执行结果为:












有时还会出现null,


       带着种种不解,来看ArrayList添加流程:

       首先,ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存。

       对于ArrayList而言,它实现List接口、底层使用数组保存所有元素。其操作基本上是对数组的操作。

       1、程序中报错的 at java.util.ArrayList.elementData(ArrayList.java:400) 和 at java.util.ArrayList.add(ArrayList.java:441),它们同属Add()方法。   

       源码如下:                                                                                                                                                                       

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


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


    函数体中,modCount是数组发生size更改的次数。然后if判断,如果数组长度小于默认的容量10,则调用扩大数组大小的方法grow()。
   
    函数grow()解释了基于数组的ArrayList是如何扩容的。数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5倍。
       接下来回到Add()函数,继续执行,elementData[size++] = e; 这行代码就是问题所在,当添加一个元素的时候,它可能会有两步来完成:1. 在 elementData[Size] 的位置存放此元素;2. 增大 Size 的值。
       在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
       而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。那好,我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。这就解释了为何集合中会出现null。
       但是数组下标越界还不能仅仅依靠这个来解释。我们观察发生越界时的数组下标,分别为10、15、22、33、49和73。结合前面讲的数组自动机制,数组初始长度为10,第一次扩容为15=10+10/2,第二次扩容22=15+15/2,第三次扩容33=22+22/2...以此类推,我们不难发现,越界异常都发生在数组扩容之时。
       由此给了我想法,我猜想是,由于没有该方法没有同步,导致出现这样一种现象,用第一次异常,即下标为15时的异常举例。当集合中已经添加了14个元素时,一个线程率先进入add()方法,在执行ensureCapacityInternal(size + 1)时,发现还可以添加一个元素,故数组没有扩容,但随后该线程被阻塞在此处。接着另一线程进入add()方法,执行ensureCapacityInternal(size + 1),由于前一个线程并没有添加元素,故size依然为14,依然不需要扩容,所以该线程就开始添加元素,使得size++,变为15,数组已经满了。而刚刚阻塞在elementData[size++] = e;语句之前的线程开始执行,它要在集合中添加第16个元素,而数组容量只有15个,所以就发生了数组下标越界异常!
0
0
查看评论

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

最近在看《实战Java虚拟机》一书,看到有关锁与并发章节时,看到如下一个多线程使用ArrayList的例子:        两个线程t1和t2同时向numberList中添加数据,由于ArrayList是线程不安全的,因此会导致添加的数据有错误,这个我...
  • u010010428
  • u010010428
  • 2016-04-27 10:24
  • 5429

ArrayList在添加元素的时候会出现的一些线程不安全的现象

再学习多线程编程的时候,看到如下代码。 package com.cbf4life; import java.util.*; public class ThreadSafeDemo { public ThreadSafeDemo() { ThreadGroup gr...
  • a1522365779
  • a1522365779
  • 2017-06-06 11:11
  • 328

多线程中ArrayList调用Add()添加元素时的下标越界问题(java.lang.ArrayIndexOutOfBoundsException)

问题:在多线程中使用ArrayList调用Add()添加元素时,有时候会出现下面的错误Exception in thread "Thread-1" Exception in thread "Thread-2" java.lang.ArrayIndexOutOf...
  • xx326664162
  • xx326664162
  • 2017-12-09 18:34
  • 330

ArrayList并发add()可能出现数组下标越界异常 | 10+10<20

ArrayList内部,add()分两步操作,step1检查array容量,step2塞值并将size+1。 假设有2个线程操作同一个ArrayList,且array的容量刚好还可以存一个值。Thread1执行add()的step1后被挂起,这时Thread1“认为”array还有位置可以塞值,然后...
  • m0_37500211
  • m0_37500211
  • 2017-12-20 00:39
  • 157

ArrayList.add()添加重复元素

这两天被这个问题折磨的死去活来。 我创建了一个全局变量ArrayList> a 和另一个全局变量ArrayList b,往a中添加b,最后发现添加的数据都是一样的。一开始我以为是我b没有clean(),后来搞了半天,返现输出竟然都是添加最后一个数据。 网上找了找关于ArrayList.add(...
  • onepiece2
  • onepiece2
  • 2015-11-09 11:46
  • 2839

java ArrayList add()方法数据被覆盖问题

java ArrayList集合添加数据覆盖问题
  • null0420
  • null0420
  • 2017-03-03 19:22
  • 793

ArrayList.add()添加一个元素后,会把前面一个元素覆盖

今天写程序时,每次取值均正确,但是不管怎么输出,都是输出最后一次 add 的值,百思不得其解,后来才发现,java的list的 add方法引用的是地址,如果是全局的话,每次引用的值都一样,取的值也就一样了。每次add都用new一个新实体就可以解决问题了,接近两小时的青春浪费了,就因为一行代码位置错误...
  • u010887744
  • u010887744
  • 2015-08-20 17:47
  • 5037

Java ArrayList add(index,element) 方法插入元素到数组指定位置

今天在开发项目的过程中,准备使用ArrayList 的 add(index,element) 来插入元素,天真的以为这样能给list排序 简略代码如下: List list = new ArrayList(50); list.add(0,element); li...
  • ForeverCjl
  • ForeverCjl
  • 2013-10-10 13:09
  • 26018

数组下标越界异常:Java.lang.ArrayIndexOutOfBoundsException

这是一个非常常见的异常,从名字上看是数组下标越界错误,解决方法就是查看为什么下标越界。 下面是一个错误示例:  Exception in thread "main" Java.lang.ArrayIndexOutOfBoundsException: 2...
  • taoxfei
  • taoxfei
  • 2017-10-10 16:42
  • 388

关于weblogic 12c 部署ear应用包时 出现Exception: 48188的一种解决方法

在weblogic12C下部署oa应用  总是报 java.lang.ArrayIndexOutOfBoundsException: 48188 java.lang.ArrayIndexOutOfBoundsException: 48188 at...
  • jiongzhizhe
  • jiongzhizhe
  • 2016-02-26 14:18
  • 2588
    个人资料
    • 访问:27358次
    • 积分:769
    • 等级:
    • 排名:千里之外
    • 原创:43篇
    • 转载:34篇
    • 译文:1篇
    • 评论:10条
    文章分类
    最新评论