大家都知道 ArrayList是自动扩容的。 那为什么会存在越界问题?

话不多说 上代码

1 packagetest;2

3 importjava.util.ArrayList;4

5 public classThreadUnSafe {6 public static ArrayList numberList= new ArrayList();7 public static class addToList implementsRunnable{8 int startNum=0;9 public addToList(intstartNum){10 this.startNum=startNum;11 }12 @Override13 public voidrun(){14 int count=0;15 while (count<50){16 try{17 Thread.sleep(100);18 }catch(InterruptedException e){19 e.printStackTrace();20 }21 numberList.add(startNum);22 System.out.println(Thread.currentThread().getName()+"=="+"第"+(count+1)+"次进入,添加的数子为"+numberList.get(numberList.size()-1)+"---此时集合大小为:"+numberList.size());23 startNum+=2;24 count++;25 }26 }27 }28

29 public static void main(String[] args) throwsInterruptedException{30 Thread t1=new Thread(new addToList(0));31 Thread t2=new Thread(new addToList(1));32 t1.start();33 t2.start();34

35 }36

37 }

测试结果:

Thread-1==第1次进入,添加的数字为1---此时集合大小为:1

Thread-0==第1次进入,添加的数字为1---此时集合大小为:1

Thread-0==第2次进入,添加的数字为1---此时集合大小为:2

Thread-1==第2次进入,添加的数字为2---此时集合大小为:3

Thread-0==第3次进入,添加的数字为2---此时集合大小为:4

Thread-1==第3次进入,添加的数字为3---此时集合大小为:5

Thread-0==第4次进入,添加的数字为4---此时集合大小为:7

Thread-1==第4次进入,添加的数字为4---此时集合大小为:7

Thread-0==第5次进入,添加的数字为4---此时集合大小为:8

Thread-1==第5次进入,添加的数字为5---此时集合大小为:9

Thread-1==第6次进入,添加的数字为6---此时集合大小为:10

Thread-0==第6次进入,添加的数字为6---此时集合大小为:10

Thread-0==第7次进入,添加的数字为6---此时集合大小为:11

Thread-1==第7次进入,添加的数字为7---此时集合大小为:12

Thread-1==第8次进入,添加的数字为8---此时集合大小为:14

Thread-0==第8次进入,添加的数字为8---此时集合大小为:14

Thread-1==第9次进入,添加的数字为9---此时集合大小为:15

Thread-0==第9次进入,添加的数字为8---此时集合大小为:16

Thread-1==第10次进入,添加的数字为10---此时集合大小为:17

Thread-0==第10次进入,添加的数字为9---此时集合大小为:18

Thread-0==第11次进入,添加的数字为10---此时集合大小为:19

Thread-1==第11次进入,添加的数字为11---此时集合大小为:20

Thread-0==第12次进入,添加的数字为11---此时集合大小为:21

Thread-1==第12次进入,添加的数字为11---此时集合大小为:21

Thread-1==第13次进入,添加的数字为13---此时集合大小为:22

Thread-0==第13次进入,添加的数字为12---此时集合大小为:23

Thread-1==第14次进入,添加的数字为14---此时集合大小为:25

Thread-0==第14次进入,添加的数字为14---此时集合大小为:25

Thread-1==第15次进入,添加的数字为15---此时集合大小为:26

Thread-0==第15次进入,添加的数字为15---此时集合大小为:26

Thread-0==第16次进入,添加的数字为15---此时集合大小为:28

Thread-1==第16次进入,添加的数字为15---此时集合大小为:28

Thread-1==第17次进入,添加的数字为16---此时集合大小为:29

Thread-0==第17次进入,添加的数字为16---此时集合大小为:29

Thread-0==第18次进入,添加的数字为18---此时集合大小为:31

Thread-1==第18次进入,添加的数字为18---此时集合大小为:31

Thread-0==第19次进入,添加的数字为18---此时集合大小为:32

Thread-1==第19次进入,添加的数字为18---此时集合大小为:32

Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.ArrayIndexOutOfBoundsException: 33

at java.util.ArrayList.elementData(ArrayList.java:422)

at java.util.ArrayList.get(ArrayList.java:435)

at test.ThreadUnSafe$addToList.run(ThreadUnSafe.java:22)

at java.lang.Thread.run(Thread.java:748)

java.lang.ArrayIndexOutOfBoundsException: 33

at java.util.ArrayList.add(ArrayList.java:463)

at test.ThreadUnSafe$addToList.run(ThreadUnSafe.java:21)

at java.lang.Thread.run(Thread.java:748)

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

其中:at java.util.ArrayList.add(ArrayList.java:463)的源代码

public booleanadd(E e) {

ensureCapacityInternal(size+ 1); //Increments modCount!!

elementData[size++] =e;return true;

}

private void ensureCapacityInternal(int minCapacity) {

ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));

}

private void ensureExplicitCapacity(int minCapacity) {

modCount++;

// overflow-conscious code

if (minCapacity - elementData.length > 0)

grow(minCapacity);

}

函数体中,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。这就是“线程不安全”了

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

java 判断list是否越界_关于ArrayList的越界问题?相关推荐

  1. java判断括号是否闭合_用 java 判断 括号是否完全匹配

    用 java 判断 括号是否完全匹配: package emp; import java.util.Stack; public class Parentheses { public static vo ...

  2. java判断是否第一次出现_利用java判断字符首次出现的位置,java替换最后一个特定字符...

    利用java判断字符首次出现的位置利用爪哇判断字符首次出现的位置, 目的: (学习视频分享:java视频教程 实现代码如下: 导入Java.util.收藏品: 导入Java.util.LinkedLi ...

  3. java 判断视频是否损坏_内容研究(或如何判断您的内容是否损坏)

    java 判断视频是否损坏 您的内容效果如何? (How well is your content working?) In the age of data-worship, most site ow ...

  4. JAVA 判断简单密码算法_十道简单算法题二【Java实现】

    前言 清明不小心就拖了两天没更了-- 这是十道算法题的第二篇了-上一篇回顾:十道简单算法题 最近在回顾以前使用C写过的数据结构和算法的东西,发现自己的算法和数据结构是真的薄弱,现在用Java改写一下, ...

  5. java判断所有域名后缀_使用Java的IO操作,提取全世界所有的三位域名后缀

    一.  前言 最近又想注册域名,便从TLD List 中下载了已经收录的全世界所有的域名后缀,然后使用Java的IO相关操作,将所有的三位域名后缀提取了出来.先给出下载文件: 所有域名后缀 二.  代 ...

  6. java 判断非ascii字符_文件名中的JavaMail和非ASCII字符

    我可以在 JavaMail中发送具有非ascii文件名的附件,但我无法下载它们.我特意为那些文件名包含非ascii字符的附件获取java.io.FileNotFoundException. 仅供参考: ...

  7. java 判断手机运营商_如何用java判断手机号运营商?

    如何用java实现判断手机号的运营商?因为每个号段都是工信部规定划分给指定运营商的,所以我们可以通过手机号码的号段来判断. 现在手机号的号段那么多,要怎样方便的的判断呢?于是我们就想到了正则表达式,在 ...

  8. java判断string是数字_(转)java判断string变量是否是数字的六种方法小结

    1.用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = 0; i < str.length(); i++){ ...

  9. java判断当前浏览器类型_[Java教程]判断用户请求时使用的浏览器类型

    [Java教程]判断用户请求时使用的浏览器类型 0 2016-09-01 15:00:04 在进行微信公众账号开发的时候,其中很大一块是微站点的开发,我们需要知道当前的浏览器是微信内置的浏览器,那么如 ...

最新文章

  1. QIIME 2教程. 19使用q2-vsearch聚类ASVs为OTUs(2021.2)
  2. 小白学python,零基础学Python难不难?
  3. 神经网络基础之可视化和交互式指南
  4. 数据库性能优化—数据库连接池
  5. UVA - 12569 Planning mobile robot on Tree (EASY Version) BFS
  6. Java使用JDBC连接随意类型数据库(mysql oracle。。)
  7. caffe框架翻译-理解(转载)
  8. J2EE第五课Servlet随课笔记
  9. Java日历compareTo()方法与示例
  10. 第十节(补课):函数的扩展 — 箭头函数的this使用
  11. 百年古董电影秒变4K高清、60FPS,AI插值,还能着色
  12. 30 道 MySQL 面试题全放送!
  13. Android实例-利用WebBrowser实现浏览器(XE8+小米2)
  14. 非受检异常(运行时异常)和受检异常的区别等
  15. 实验2-1-1 计算摄氏温度 (5 分)
  16. 大疆DJI Thermal SDK Linux libdirp.so: cannot open shared object file: No such file or directory
  17. 用PHP ping 一个 IP
  18. Hadoop MapReduce实现人员二度关系运算
  19. Nodejs+MongoDB+WebRTC搭建视频通话协同应用
  20. UE4蓝图(很经典) 间隔1秒就执行一次,间隔循环执行

热门文章

  1. 手机短信验证码发送(106短信验证码发送)
  2. jQuery.on() 函数详解
  3. 用手机在家做韩剧剪辑,无需露脸,一周1300
  4. 模糊搜索——Vue单页面-Element UI
  5. Django网站实例效果
  6. 京东CPS商品推广接入流程
  7. 从一路赞美到嘘声不断 90后创业热潮已宣告死亡
  8. 数据库事务的四大特性:ACID
  9. 分类:基于规则的分类——RIPPER算法
  10. PHP微信扫描登陆(模拟微信企业号平台登陆)