Java中的arrayList为什么是线程不安全的
简介
在面试的时候就会经常问到,arrylist安全吗
arraylist是线程不安全的我们都知道,但是具体为什么arraylist是不安全的线程,大部分人都不怎么了解
list接口下满有两个实现,一个是arraylist,另外一个是vector
从源码的角度来看,因为vector的方法前加了synchronize关键字,设计的时候希望vector是线程安全的,而希望arrylist是高校的(安全和高效不能兼顾)
不安全的案例
在多个线程进行add操作时可能会导致elementData数组越界
public class UnSageList { public static void main(String[] args) { ArrayList<String> arrayList = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(() -> arrayList.add(Thread.currentThread().getName())).start(); } try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(arrayList.size()); } }
正常情况下,遍历10000次list的size应该是10000,但是基本上是不可能达到10000的
原因:
在多个线程进行add操作会,触发数组扩容,在扩容的时候可能会发生数据的覆盖
加入现在minCapacity为10,假设已经添加了9个数据,size是9,接下来添加数据就需要进行扩容操作
- 线程a执行完add函数的ensureCapacityInternal(size + 1)中的ensureExplicitCapacity操作,发现需要扩容,于是进行了grow扩容操作
- 这个时候线程b开始执行add操作,但是检验发现数组不需要扩容于是执行add中的elementData[size++] = e操作,把数字放到了9的位置,然后size++,这个时候size为10
- 线程a接着执行,尝试数字放到9的位置(因为在线程a中,a的数据还没有更新),这个时候就造成了数据的覆盖,丢失了一个数据
解释一下为什么会发生重复添加到一个位置
Java线程模型由主内存和工作内存组成:
说明:
- 工作内存和主内存两部分一起组成Java线程的内存模型
- 工作内存是属于线程的,不同线程的工作内存之间不可共享,不可通讯
- 工作内存通过load操作从主内存中读取数据,通过save操作将数据写入主内存
- 线程之间的通讯:本质上是通过主内存的数据共享
所以多个线程可能同时加载到了一个size数据,但是线程a在进行扩容操作后,还没有完成赋值和size++的操作,另外的一个线程b就进行了add操作,发现不需要扩容就直接赋值,当线程a在进行赋值操作的时候就会发生数据覆盖
让线程安全
加上synchronized 块
public class UnSageList { public static void main(String[] args) { ArrayList<String> arrayList = new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(() -> { synchronized (arrayList){ arrayList.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(arrayList.size()); } }
本文地址:https://blog.csdn.net/weixin_42779370/article/details/108029230
上一篇: Java微服务项目(maven父子级项目)怎么打包
下一篇: springboot编写批量删除
推荐阅读
-
象棋中的卒为什么是五个 这里面到底有什么典故
-
象棋中为什么是红棋子先走 其实里面是有典故的
-
vue组件中的data为什么是函数?
-
Vue组件中的Data为什么是函数。
-
Java中的容器(集合)之ArrayList源码解析
-
为什么说陈诚是蒋介石政权中的特殊人物,老蒋的一张底牌
-
微信小程序授权 获取用户的openid和session_key【后端使用java语言编写】,我写的是get方式,目的是测试能否获取到微信服务器中的数据,后期我会写上post请求方式。
-
举例说明Java多线程编程中读写锁的使用
-
艾因贾鲁战役中蒙古是怎么惨败的?马穆鲁克为什么如此强
-
在IOS中为什么使用多线程及多线程实现的三种方法