浅析Java并发编程:如何做到线程安全-Collections.synchronizedList和CopyOnWriteArrayList的选择
2021/9/9 20:34:48
本文主要是介绍浅析Java并发编程:如何做到线程安全-Collections.synchronizedList和CopyOnWriteArrayList的选择,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
在多线程环境下ArrayList是线程不安全的,所以需要使用线程安全的List,我第一时间使用的是Collections.synchronizedList(new Arraylist<>()),但是在实际使用过程中却发生了安全事件。
一、使用Collections.synchronizedList(new Arraylist<>()) + 同步解决线程安全问题
当前存在一个类IntegerList,该类对象在多线程环境中添加元素和获取获取
public class IntegerList { final private List<Integer> integerList; public IntegerList(List<Integer> integerList) { this.integerList = integerList; } /* * 获取列表中小于integer的所有数 * */ public List<Integer> getLessThanInteger(Integer integer){ List<Integer> result=new ArrayList<>(); for (Integer item : integerList) { if(item<=integer){ result.add(item); } } return result; } private void addInteger(Integer integer){ integerList.add(integer); } @Override public String toString() { return "IntegerList{" + "integerList=" + integerList + '}'; } }
使用ArrayList 线程不安全会报错ConcurrentModificationException
public static void main(String[] args) { IntegerList integerList = new IntegerList(new ArrayList<>()); for (int i = 0; i < 100; i++) { final int i1=i; new Thread(()->{ //添加元素 integerList.addInteger(i1); //获取比i1小于等于所有数 List<Integer> lessThanInteger = integerList.getLessThanInteger(i1); }).start(); } }
这个是单线程也会报错的,原因是:添加的元素的同时,又获取元素,所以不安全。
报错原因是在getLessThanInteger中使用增强for循环遍历了列表,集合的增强for循环是使用Iterator实现的,遍历过程中如果发现其他线程有修改列表就会抛异常
编译后Class文件
Iterator next()进行修改检查,如果集合被修改过,就会抛异常
使用线程安全的Collections.synchronizedList 依然会报错
public static void main(String[] args) { IntegerList integerList = new IntegerList( Collections.synchronizedList(new ArrayList<>())); for (int i = 0; i < 100; i++) { final int i1=i; new Thread(()->{ //添加元素 integerList.addInteger(i1); //获取比i1小于等于所有数 List<Integer> lessThanInteger = integerList.getLessThanInteger(i1); }).start(); } }
为什么线程安全的Collections.synchronizedList在多线程下还是会报错?
原因在于返回的迭代器是线程不安全的,在jdk源码里面注释说明,要用户自己使用同步来控制遍历迭代器
使用同步改造:加入同步代码块,并且锁定的对象是 integerList
这样就可以成功执行了。
二、使用CopyOnWriteArrayList 完美解决迭代过程不安全问题
public static void main(String[] args) { IntegerList integerList = new IntegerList(new CopyOnWriteArrayList<>()); for (int i = 0; i < 100; i++) { final int i1 = i; new Thread(() -> { //添加元素 integerList.addInteger(i1); //获取比i1小于等于所有数 List<Integer> lessThanInteger = integerList.getLessThanInteger(i1); }).start(); } }
CopyOnWriteArrayList 解决线程安全的原因。CopyOnWriteArrayList 获取元素的时候使用的是当前数组Array的一个快照,而修改元素的时候,会复制一份Array再进行修改,修改不会对原本的Array产生影响,修改完后会覆盖原本的Array。
修改元素:
迭代器:
我们在使用线程安全的List应该使用CopyOnWriteArrayList,防止在遍历迭代器过程中出现异常,缺点是CopyOnWriteArrayList会使用额外的内存空间。
原文链接:https://blog.csdn.net/piexie8364/article/details/115731723
这篇关于浅析Java并发编程:如何做到线程安全-Collections.synchronizedList和CopyOnWriteArrayList的选择的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南