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。

在这里插入图片描述
那么并发不安全,怎么去解决呢。。。

解决

出现这种并发不安全的结果,主要原因是我们在多线程操作下,使用了不安全的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套哦~
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

收破烂的小熊猫~

你的鼓励将是我创造最大的东西~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值