关于集合类不安全的问题解决
List不安全
- 通过List集合类在做一些基本的操作的时候,假设在并发的情况下执行会出现并发修改异常,也就是java.util.ConcurrentModificationException,其实不光是List集合,在Set和Map类使用的情况下同样也会出现这些问题
- 对于List集合类,我们提供一些解决方案来解决并发修改异常的问题
- List list = new Vector<>(); 也就是通过Vector来解决,这个类的底层是通过synchronized同步方法完成的 ,所以可以确保并发安全。
- List list = Collections.synchronizedList(new ArrayList<>()),这套解决方案是由工具类Collections这个类底层调用的同步方法,本质也差不多,都是通过同步方法来解决并发下不安全的问题
- List list = new CopyOnWriteArrayList<>(),这个是JUC包下的解决方案,CopyOnWrite是指写入时复制 也就是 COW,是计算机程序设计领域的一种优化策略。在多个线程调用的时候,list读取的时候是固定的,但是写入的时候会存在覆盖的情况,所以通过此方法解决,这种思想也是读写分离的思想,为的就是在写入的时候避免覆盖,造成一些数据的问题,它的底层是Lock锁,推荐使用。
- 关于List不安全,我们推荐使用第三种解决方案,相比较 Vector来说,Lock锁一定比synchronized来的好一点,其具体区别可以看我的博客关于锁的运用,地址为https://blog.csdn.net/hzyzzz/article/details/106248265。接下来我展示一些测试List不安全的代码:
package com.czu.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* java.util.ConcurrentModificationException 并发修改异常
* @author 87682
*/
public class ListTest {
public static void main(String[] args) {
// List<String> list = Arrays.asList("1","2","3");
// list.forEach(System.out::println);
//并发下ArrayList是不安全的
/**
* 解决方案:
* 1. List<String> list = new Vector<>();
* 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
* 3. List<String> list = new CopyOnWriteArrayList<>();
*/
//List<String> list = new ArrayList<>();
// List<String> list = new Vector<>();
//List<String> list = Collections.synchronizedList(new ArrayList<>());
/**
* CopyOnWrite是指写入时复制 COW 计算机程序设计领域的一种优化策略
* 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
* 在写入的时候避免覆盖,造成数据问题
* 读写分离
* CopyOnWriteArrayList 比 Vector牛逼在哪里? Vector用的是synchronized CopyOnWriteArrayList用的是lock锁
*/
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i++) {
// list.add(UUID.randomUUID().toString().substring(0,5));
// System.out.println(list);
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
Set不安全
-
hashSet的底层其实就是hashMap,在并发情况下依旧会出现跟List一样的并发修改异常问题,解决方案相似:
- Set set = Collections.synchronizedSet(new HashSet<>())
- Set set = new CopyOnWriteArraySet<>()
-
由于跟之前类似,我也不再多说,看代码:
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* 同理可证: java.util.ConcurrentModificationException 并发修改异常
* 解决方案:
* 1. Set<String> set = Collections.synchronizedSet(new HashSet<>());
* 2. Set<String> set = new CopyOnWriteArraySet<>();
* @author 87682
*/
public class SetTest {
public static void main(String[] args) {
//Set<String> set = new HashSet<>();
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 1; i <= 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
Map不安全
- 其实在工作中我们一般不会去使用HashMap,原因也很简单,并发下的问题很大,它默认等价于new HashMap<>(16,0.75),两个参数分别为加载因子和初始化容量,正常情况下,我们会使用ConcurrentHashMap,代码如下:
package com.czu.unsafe;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* java.util.ConcurrentModificationException 并发修改异常
* 研究ConcurrentHashMap的原理
* @author 87682
*/
public class MapTest {
public static void main(String[] args) {
/**
* map 是这样用的吗? 不是 ,工作中不用HashMap
* 默认等价于什么? new HashMap<>(16,0.75);
* 加载因子 、 初始化容量
*/
//Map<String,String> map = new HashMap<>();
Map<String,String> map = new ConcurrentHashMap<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
总之多线程的并发下会产生很多问题,关于ConcurrentHashMap,这里没有过多解释,因为需要很多关于Map的基础,有兴趣的也可以研究一下它的源码,总之我提供了一些集合类不安全的解决方案,都是在JUC包下的方案,对于线程安全的问题应该很有帮助。