【Java】ArrayList线程不安全的坑
2023-04-18 15:18:32 时间
问题复现:
使用Java的steam().paralleStream(),foreach()方法向ArrayList添加数据,导致ArrayList中出现空值,代码如下:
public static void main(String[] args) { List<Integer> a = new ArrayList<>(); for (int i = 0; i < 20; i++) { a.add(i); } List<String> a1 = new ArrayList<>(); a.parallelStream().forEach(x -> { a1.add(String.valueOf(x)); }); System.out.println(a1); }
输出结果:
[12, 6, 14, 5, 13, 8, 11, 9, 10, 7, 2, 17, 1, 4, 0, null, 19, 18, 16, 15]
如上,会出现null值
问题分析:
ArrayList通过size变量来控制当前数组元素的指针,代码:
private void add(E e, Object[] elementData, int s) { if (s == elementData.length) { elementData = this.grow(); } elementData[s] = e; this.size = s + 1; } public boolean add(E e) { ++this.modCount; this.add(e, this.elementData, this.size); return true; }
如果此时出现多线程操作,例如Thread1获取到size=3,对数组的第三个元素进行赋值,此时Thread2获取到的size也等于3,也对数组第三个元素进行赋值,此时Thread1和Thread2都操作的第三个元素,然后此时Thread1进行size+1操作,Thread2进行加size+1操作,当前size值为5,但是实际数组中只有4个元素,有一个为null,因为Thread2本来应该对数组第4个元素赋值,因为线程不安全导致拿到的size值为3
解决方式:
1.使用Vector,所有方法用synchronized修饰,数组结构
2.Collections.sychronizedList(list)的方式获取一个list的代理,不限于ArrayList,可以实现任意list子类的同步,对大部分操作代码块进行加锁,例如:add,set,get,foreach等,不过迭代器操作未加锁,依旧是线程不安全的
3.使用Java集合的stream().map().collect(Collectors.toList())方法,利用Fork/Join
的分治,每个线程会创建一个独立list进行操作,最后合并
4.CopyOnWriteArrayList,写操作单独创建一个list,读操作不加锁,适合读操作较多的场景
相关文章
- 【技术种草】cdn+轻量服务器+hugo=让博客“云原生”一下
- CLB运维&运营最佳实践 ---访问日志大洞察
- vnc方式登陆服务器
- 轻松学排序算法:眼睛直观感受几种常用排序算法
- 十二个经典的大数据项目
- 为什么使用 CDN 内容分发网络?
- 大数据——大数据默认端口号列表
- Weld 1.1.5.Final,JSR-299 的框架
- JavaFX 2012:彻底开源
- 提升as3程序性能的十大要点
- 通过凸面几何学进行独立于边际的在线多类学习
- 利用行动影响的规律性和部分已知的模型进行离线强化学习
- ModelLight:基于模型的交通信号控制的元强化学习
- 浅谈Visual Source Safe项目分支
- 基于先验知识的递归卡尔曼滤波的代理人联合状态和输入估计
- 结合网络结构和非线性恢复来提高声誉评估的性能
- 最佳实践丨云开发CloudBase多环境管理实践
- TimeVAE:用于生成多变量时间序列的变异自动编码器
- 具有线性阈值激活的神经网络:结构和算法
- 内网渗透之横向移动 -- 从域外向域内进行密码喷洒攻击