集合类的安全问题,主要分为单线程和多线程情况下。
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套哦~