首先需要了解什么是线程安全:线程安全就是说多线程访问同一代码(对象、变量等),不会产生不确定的结果。 既然说ArrayList是线程不安全的,那么在多线程中操作一个ArrayList对象,则会出现不确定的结果。具体是怎样不确定,请看测试下面这段代码(在此测试ArrayList的add方法):

public class ArrayListInThread implements Runnable{

//线程不安全
private List threadList = new ArrayList();
//线程安全
//private List threadList = Collections.synchronizedList(new ArrayList());

@Override
public void run() {
try {
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
//把当前线程名称加入list中
threadList.add(Thread.currentThread().getName());
}

public static void main(String[] args) throws InterruptedException{
ArrayListInThread listThread = new ArrayListInThread();

for(int i = 0; i < 100; i++){
Thread thread = new Thread(listThread, String.valueOf(i));
thread.start();
}

//等待子线程执行完
Thread.sleep(2000);

System.out.println(listThread.threadList.size());
//输出list中的值
for(int i = 0; i < listThread.threadList.size(); i++){
if(listThread.threadList.get(i) == null){
System.out.println();;
}
System.out.print(listThread.threadList.get(i) + " ");
}
}
}
执行几次会发现结果不一样,甚至有时会出现ArrayIndexOutOfBoundsException,贴出几次执行的结果:

结果一:

结果二:

结果三:

以上执行结果说明ArrayList确实是线程不安全的,然后我们从线程并发的角度分析,为何会出现这样的结果:

首先,我们先来看一下ArrayList中的add方法是如何实现的

//添加元素e
public boolean add(E e) {
// 确定ArrayList的容量大小
ensureCapacity(size + 1); // Increments modCount!!
// 添加e到ArrayList中
elementData[size++] = e;
return true;
}

// 确定ArrarList的容量。
// 若ArrayList的容量不足以容纳当前的全部元素,设置 新的容量=“(原始容量x3)/2 + 1”
public void ensureCapacity(int minCapacity) {
// 将“修改统计数”+1,该变量主要是用来实现fail-fast机制的
modCount++;
int oldCapacity = elementData.length;
// 若当前容量不足以容纳当前的元素个数,设置 新的容量=“(原始容量x3)/2 + 1”
if (minCapacity > oldCapacity) {
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1;
//如果还不够,则直接将minCapacity设置为当前容量
if (newCapacity < minCapacity)
newCapacity = minCapacity;
elementData = Arrays.copyOf(elementData, newCapacity);
}
}

结果中,有的值没有出现(结果一中3没有出现),有的出现了null值,这是由于赋值时出现了覆盖。赋值语句为:elementData[size++] = e,这条语句可拆分为两条:
1. elementData[size] = e;
2. size ++;
假设A线程执行完第一条语句时,CPU暂停执行A线程转而去执行B线程,此时ArrayList的size并没有加一,这时在ArrayList中B线程就会覆盖掉A线程赋的值,而此时,A线程和B线程先后执行size++,便会出现值为null的情况;至于结果三中出现的ArrayIndexOutOfBoundsException异常,
则是A线程在执行ensureCapacity(size+1)后没有继续执行,此时恰好minCapacity等于oldCapacity,B线程再去执行,同样由于minCapacity等于oldCapacity,ArrayList并没有增加长度,B线程可以继续执行赋值(elementData[size] = e)并size ++也执行了,此时,CPU又去执行A线程的赋值操作,由于size值加了1,size值大于了ArrayList的最大长度,
因此便出现了ArrayIndexOutOfBoundsException异常。

既然ArrayList是线程不安全的,但如果需要在多线程中使用,可以采用list<Object> list =Collections.synchronizedList(new ArrayList<Object>)来创建一个ArrayList对象。

原文:https://blog.csdn.net/zhangxin961304090/article/details/46804065

ArrayList为什么是线程不安全的的更多相关文章

  1. Day10_53_Collections.synchronizedList() 将Arraylist集合转换为线程安全的集合

    将Arraylist集合转换为线程安全的集合 import java.util.ArrayList; import java.util.Collections; import java.util.Li ...

  2. ArrayList如何实现线程安全

    一:使用synchronized关键字,这个大家应该都很熟悉了,不解释了: 二:使用Collections.synchronizedList();使用方法如下: 假如你创建的代码如下:List< ...

  3. ArrayList如何保证线程安全

    ArrayList是线程不安全的,轻量级的.如何使ArrayList线程安全? 1.继承Arraylist,然后重写或按需求编写自己的方法,这些方法要写成synchronized,在这些synchro ...

  4. 论 ArrayList如何实现线程安全

    一:使用synchronized关键字 二:使用Collections.synchronizedList(); 假如你创建的代码如下:List<Map<String,Object>& ...

  5. 在JAVA中ArrayList如何保证线程安全

    [b]保证线程安全的三种方法:[/b]不要跨线程访问共享变量使共享变量是final类型的将共享变量的操作加上同步一开始就将类设计成线程安全的, 比在后期重新修复它,更容易.编写多线程程序, 首先保证它 ...

  6. 为什么ArrayList、LinkedList线程不安全,Vector线程安全

    ArrayList源码 public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! ele ...

  7. ArrayList/Vector的原理、线程安全和迭代Fail-Fast

    疑问 * ArrayList是非线程非安全的,具体是指什么?具体会产生什么问题?* ArrayList的内部原理是什么?为什么可以动态扩容?* Vector是线程安全的,具体是如何实现的?为什么不再推 ...

  8. 为什么说ArrayList是线程不安全的?

    一.概述 对于ArrayList,相信大家并不陌生.这个类是我们平时接触得最多的一个列表集合类. 面试时相信面试官首先就会问到关于它的知识.一个经常被问到的问题就是:ArrayList是否是线程安全的 ...

  9. 容器之List接口下各实现类(Vector,ArrayList 和LinkedList)的线程安全问题

    Vector .ArrayList 和LinkedList都是List接口下的实现类,但是他们之间的区别和联系是什么呢? 首先: 然后: 如果您仅仅想知道结论,那么可以关闭了. 下面我讨论讨论为什么. ...

随机推荐

  1. vmware虚拟机设置时区、时间

    首先查看时间发现和百度的时间不一样 [root@www ~]# dateWed Dec 5 14:00:32 CST 2018 1.配置ntp服务器,设置时区同步,请参照ntp篇  https://w ...

  2. 简单通讯聊天 群聊功能 Windows下的客户端 Linux下的epoll服务器

    1 服务器代码  Linux eclipse C++ //======================================================================= ...

  3. Day03:数组 、 继承的意义(上)

    继承 面向对象编程三(四)大特征 (抽象),封装,继承,多态什么是继承? 继承是面向对象编程中一种代码复用的方式为什么需要继承? 减少代码冗余,提高程序的可维护性和可扩展性怎样使用继承? 语法: cl ...

  4. P2429 【制杖题】

    这题目名字也是够了... emmmmmm为什么要用线筛??????不感觉很麻烦吗??????既然是智障制杖题,那么肯定要用很简单的算法啦~下面,我就提供一种非常便于理解的膜你算法~~~很明显,做了这题 ...

  5. 【安卓开发】一个简单快递查询APP实例的实现摘要

    前言 做毕业设计涉及到安卓开发,决定好好学习安卓开发.在正式做毕业设计之前,有必要先设计和完成一个与毕业设计最终成果相关的demo或者说样例APP.最终毕业设计需要实现的功能包括通过调用PHP端API ...

  6. Akka系列(八):Akka persistence设计理念之CQRS

    前言........ 这一篇文章主要是讲解Akka persistence的核心设计理念,也是CQRS(Command Query Responsibility Segregation)架构设计的典型 ...

  7. tableau desktop

    参考: 入门指南: https://help.tableau.com/current/guides/get-started-tutorial/zh-cn/get-started-tutorial-co ...

  8. oracle sid_name service_name

    在工作中也遇到了这种情况,使用oracle这么长时间一直使用的都是SID的概念.也是给自己扩充了一下知识,所以后面在配置oracle数据库连接的时候需要确认拿到的是service还是sid再进行相应的 ...

  9. P4868 Preprefix sum

    传送门 挺显然的一题?单点修改,前缀和数组前缀查询 树状数组就可以维护了 考虑每个位置对应询问的贡献,设询问的位置为 $x$,对于原数组 $a[i]$ 的某个位置 $i$,它会贡献 $(x-i+1)* ...

  10. ds replicas是什么

    以上是我百度的答案.很显然,这个对于初学者很不友好,中文的每一个靠谱的,英文的都TM是how 和 why .But 我要 what! 所以只能自己搞,自己琢磨,搞完分享出来,助力后来者不掉坑,有所查. ...