集合类的安全问题,主要分为单线程和多线程情况下。
1、List不安全
list不安全
单线程:安全
import java.util.Arrays;
import java.util.List;
/**
* @className:
* @PackageName: com.jp.testlist
* @author: youjp
* @create: 2020-05-04 20:48
* @description: TODO 单线程下:测试list是否安全
* @Version: 1.0
*/
public class TestList {
public static void main(String[] args) {
List<String> list= Arrays.asList("a","b","c");
list.forEach(System.out::println);
}
}
多线程:不安全
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @className:
* @PackageName: com.jp.testlist
* @author: youjp
* @create: 2020-05-05 10:58
* @description: TODO 测试多线程下,List集合是否安全
* @Version: 1.0
*/
public class TestList2 {
public static void main(String[] args) {
// 测试多线程下是否安全List,3条线程都不安全了
// 多线程下记住一个异常,并发修改异常 java.util.ConcurrentModificationException
List<String> list = new ArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
测试多线程下,list集合是否安全,发现3条线程就不安全了,并且有报并发修改异常:java.util.ConcurrentModificationException。
那么并发不安全,怎么去解决呢。。。
解决
出现这种并发不安全的结果,主要原因是我们在多线程操作下,使用了不安全的list集合。那么是不是我们使用了并发下安全的集合就可以了呢。这里有三种安全处理的方式:
1.使用Vector集合。 Vector是一个线程安全的类,效率低下 。不推荐使用
List<String> list = new Vector<>();
2.使用Collections.synchronizedList方法,中等。
List<String> list = Collections.synchronizedList(new ArrayList<>());
3.使用JUC下的CopyOnWriteArrayList,推荐使用,最佳。
List<String> list = new CopyOnWriteArrayList<>(); // JUC 100 推荐使用
完整代码:
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @className:
* @PackageName: com.jp.testlist
* @author: youjp
* @create: 2020-05-05 10:58
* @description: TODO 解决多线程下,使用List集合出现的并发修改异常
* 故障现象: ConcurrentModificationException
* 导致原因: 多线程操作集合类不安全
* 3种解决方案:
* * 1 .List<String> list = new Vector<>(); // Vector 是一个线程安全的类,效率低下
* * 2 .List<String> list = Collections.synchronizedList(new ArrayList<>());
* * 3. List<String> list = new CopyOnWriteArrayList<>(); // JUC 100 推荐使用
* @Version: 1.0
*/
public class TestList2 {
public static void main(String[] args) {
// 写入时复制; COW 思想,计算机设计领域。优化策略
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
解读
1、多线程高并发程序中,一致性最为重要
2、CopyOnWriteArrayList写入时复制;
底层原理就是给数组添加volatile关键字(有关volatile后面章节会详细讲解) 思想: 多个调用者,想调用相同的资源;
如果线程只是去读,就不会产生锁! 如果线程是去写,就需要拷贝一份到自己那里,修改完毕后,在替换原指针!原先读的线程指针就移到新的指针。
写入时复制(COW)思想原理:指针,复制指向的问题
2、Set不安全
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @className:
* @PackageName: com.jp.testlist
* @author: youjp
* @create: 2020-05-05 11:40
* @description: TODO 测试多线程下,Set集合是否安全
* @Version: 1.0
*/
public class TestSet {
public static void main(String[] args) {
Set<String> set=new HashSet<>();
for (int i = 0; i <30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
测试发现,多线程下set集合也报了并发修改异常:java.util.ConcurrentModificationException。
解决
1.使用Collections.synchronizedSet
Set<String> set= Collections.synchronizedSet(new HashSet<>());
2.使用 CopyOnWriteArraySet
Set<String> set=new CopyOnWriteArraySet<>();
完整代码
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @className:
* @PackageName: com.jp.testlist
* @author: youjp
* @create: 2020-05-05 11:40
* @description: TODO 解决测试多线程下,Set集合并发修改异常问题
* @Version: 1.0
*/
public class TestSet {
public static void main(String[] args) {
//解决方法1:
//Set<String> set= Collections.synchronizedSet(new HashSet<>());
//解决方法2:使用juc包下CopyOnWriteArraySet
Set<String> set=new CopyOnWriteArraySet<>();
for (int i = 0; i <30 ; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
3、map 不安全
测试多线程下,hashmap是否安全
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @className:
* @PackageName: com.jp.testlist
* @author: youjp
* @create: 2020-05-05 14:04
* @description: TODO 测试多线程下map是否安全
* @Version: 1.0
*/
public class TestMap {
public static void main(String[] args) {
Map<String,String> map=new HashMap();
for (int i = 0; i <30 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
测试结果意料之中,hashMap多线程下操作也是不安全的。
解决
查看了jdk官方文档,可以看到
在多线程下,我们可以使用ConcurrentHashMap来完成操作
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* @className:
* @PackageName: com.jp.testlist
* @author: youjp
* @create: 2020-05-05 14:04
* @description: TODO 解决多线程下map安全问题
* @Version: 1.0
*/
public class TestMap {
public static void main(String[] args) {
//functionOne();
functionTwo();
}
/**
* 解决方法1:使用Collections.synchronizedMap
*/
public static void functionOne(){
Map<String,String> map= Collections.synchronizedMap(new HashMap<>());
for (int i = 0; i <30 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
/**
* 解决方法2:使用juc包下ConcurrentHashMap (推荐使用)
*/
public static void functionTwo(){
Map<String,String> map= new ConcurrentHashMap<>();
for (int i = 0; i <30 ; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
有兴趣的老爷,可以关注我的公众号【一起收破烂】,回复【006】获取2021最新java面试资料以及简历模型120套哦~