关于ArrayList的越界问题?
大家都知道 ArrayList是自动扩容的。 那为什么会存在越界问题?
话不多说 上代码
package test;
import java.util.ArrayList;
public class ThreadUnSafe {
public static ArrayList<Integer> numberList= new ArrayList<Integer>();
public static class addToList implements Runnable{
int startNum=0;
public addToList(int startNum){
this.startNum=startNum;
}
@Override
public void run(){
int count=0;
while (count<50){
try{
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
numberList.add(startNum);
System.out.println(Thread.currentThread().getName()+"=="+"第"+(count+1)+"次进入,添加的数子为"+numberList.get(numberList.size()-1)+"---此时集合大小为:"+numberList.size());
startNum+=2;
count++;
}
}
}
public static void main(String[] args) throws InterruptedException{
Thread t1=new Thread(new addToList(0));
Thread t2=new Thread(new addToList(1));
t1.start();
t2.start();
}
}
测试结果:
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 boolean add(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个,所以就发生了数组下标越界异常!
关于ArrayList的越界问题?的更多相关文章
- IndexOutOfBoundsException ArrayList 访问越界
java.lang.IndexOutOfBoundsException: Index: 3, Size: 2
- JAVA - ArrayList是否会越界?
JAVA - ArrayList是否会越界? ArrayList并发add()可能出现数组下标越界异常. ArrayList是实现了基于动态数组的数据结构. LinkedList是基于链表的数据结构 ...
- 深入理解java中的ArrayList和LinkedList
杂谈最基本数据结构--"线性表": 表结构是一种最基本的数据结构,最常见的实现是数组,几乎在每个程序每一种开发语言中都提供了数组这个顺序存储的线性表结构实现. 什么是线性表? 由0 ...
- ArrayList LinkedList源码解析
在java中,集合这一数据结构应用广泛,应用最多的莫过于List接口下面的ArrayList和LinkedList; 我们先说List, public interface List<E> ...
- ArrayList源码阅读笔记(基于JDk1.8)
关键常量: private static final int DEFAULT_CAPACITY = 10; 当没有其他参数影响数组大小时的默认数组大小 private static final Obj ...
- java8 ArrayList源码阅读
转载自 java8 ArrayList源码阅读 本文基于jdk1.8 JavaCollection库中有三类:List,Queue,Set 其中List,有三个子实现类:ArrayList,Vecto ...
- java提高篇(二一)-----ArrayList
一.ArrayList概述 ArrayList是实现List接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List ...
- Java 集合 — ArrayList
ArrayList ArrayList是基于数组实现的List 是有序的 每次添加之前判断是否进行扩容 不是线程安全的. 构造方法 // 空数组 private static final Object ...
- jdk源码分析之ArrayList
ArrayList关键属性分析 ArrayList采用Object数组来存储数据 /** * The array buffer into which the elements of the Array ...
随机推荐
- 第五篇 CSS入门 明白 三种嵌套形式,三种常用控制器
CSS入门 css是 层叠式样式表 css的作用是什么呢?举个抽象的例子啊,HTML是人,CSS则是衣服... css给html穿上衣服,有三种形式: 内嵌.内联.外联. 这三种形式,优先级为 ...
- 免费安装正版Xshell6+Xftp6
先进入链接获取最新的下载地址 下载地址 填写一下姓名+邮箱,勾选需要哪个就行了 它会发给你指定邮箱一个地址,点击即可下载 安装就不用说了...
- SDRAM介绍
一. 介绍 存储器的最初结构为线性,它在任何时刻,地址线中都只能有一位有效.设容量为N×M的存储器有S0-Sn-1条地址线:当容量增大时,地址选择线的条数也要线性增多,利用地址 ...
- 为Qtcreator 编译的程序添加管理员权限
(1)创建资源文件 myapp.rc 1 24 uac.manifest (2)创建文件uac.manifest <?xml version="1.0" encoding=& ...
- MP4 ISO基础媒体文件格式 摘要 1
目录 Object-structured File Organization 1 File Type Box (ftyp) Box Structures File Structure and gene ...
- 04-spring框架—— Spring 集成 MyBatis
将 MyBatis与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring来管理.所以,该整合,只需要将 SqlSessionFactory 的对象 ...
- Django + celery +redis使用
1.安装包 pip install celery pip install django-celery pip install pymysql 2.创建一个django项目 - proj/ - proj ...
- HDU - 6396 Swordsman (单调性+贪心)
题意:有n个怪物和k种属性,当且仅当你的每种属性都大于等于怪物的属性才可以击杀它,且击杀怪物可以提升你一定的属性值.求可击杀怪物的最大数量以最终的属性值. 这不就是银行家算法里的安全性检验么? 本题的 ...
- tp5中的return
return 可以输出对象,但是不可以输出数组 class Index { public function index(Student $student) { $data = $student-> ...
- MongoDB——》聚合查询(project、match、limit、skip、unwind、group、sort)
https://blog.csdn.net/weixin_43453386/article/details/85065043#1_testweightname_id_35 https://blog.c ...