JUC并发编程学习(六)-集合类不安全

简介: JUC并发编程学习(六)-集合类不安全

集合类的安全问题,主要分为单线程和多线程情况下。

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。

20200401134307494.png

那么并发不安全,怎么去解决呢。。。

解决

出现这种并发不安全的结果,主要原因是我们在多线程操作下,使用了不安全的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)思想原理:指针,复制指向的问题

20200401134307494.png

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多线程下操作也是不安全的。

20200401134307494.png

解决

查看了jdk官方文档,可以看到

20200401134307494.png

在多线程下,我们可以使用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套哦~


相关文章
|
8月前
|
安全 算法 Java
剑指JUC原理-19.线程安全集合(上)
剑指JUC原理-19.线程安全集合
57 0
|
7月前
|
安全 Java 容器
多线程(进阶四:线程安全的集合类)
多线程(进阶四:线程安全的集合类)
45 0
|
8月前
|
安全 Java
多线程(进阶三:JUC)
多线程(进阶三:JUC)
74 0
|
8月前
|
安全 Java 数据库
剑指JUC原理-19.线程安全集合(下)
剑指JUC原理-19.线程安全集合
59 0
|
Web App开发 安全 Java
JUC高并发编程(一)——JUC基础知识
JUC高并发编程(一)——JUC基础知识
146 0
|
SpringCloudAlibaba 安全 Java
JUC并发编程(二):线程相关知识点
实现编发编程的主要手段就是多线程。线程是操作系统里的一个概念。接下来先说说两者的定义、联系与区别。
85 0
|
存储 安全 Java
【JUC基础】11. 并发下的集合类
我们直到ArrayList,HashMap等是线程不安全的容器。但是我们通常会频繁的在JUC中使用集合类,那么应该如何确保线程安全?
115 0
JUC并发编程学习(一)-什么是JUC
JUC并发编程学习(一)-什么是JUC
JUC并发编程学习(一)-什么是JUC
|
分布式计算 大数据 Java
JUC并发编程学习(十四)-任务拆分ForkJoin详解
JUC并发编程学习(十四)-任务拆分ForkJoin详解
JUC并发编程学习(十四)-任务拆分ForkJoin详解
JUC并发编程学习1:JUC概述、进程线程概述
JUC就是工具类,java.util.concurent的缩写。用来开发并发编程的工具包。 JUC是一个处理线程的工具包,JDK1.5开始出现的。
JUC并发编程学习1:JUC概述、进程线程概述