JUC下验证解决集合类不安全

简介: JUC下验证解决集合类不安全

我们平常使用HashMap,ArrayList觉得安全是因为是单线程的,它们在多线程下并不安全

JUC并发,在JUC看来,这些集合都是不安全的

  1. List不安全

单线程的情况下没有问题


package com.wyh.unSafe;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
 * @program: JUC
 * @description: 不安全的List
 * @author: 魏一鹤
 * @createDate: 2022-02-14 22:33
 **/
public class ListUnSafe {
public static void main(String[] args){
//初始化list
        List<String> list = Arrays.asList("1", "2", "3");
//forEach循环打印 forEach的参数是一个函数式接口
        list.forEach(System.out::println);
//相当于以下输出写法
        //for (String s : list) {
        //    System.out.println(s);
        //}
    }
    }


1

2

3


多线程下发现问题


package com.wyh.unSafe;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
/**
 * @program: JUC
 * @description: 不安全的List 多线程
 * @author: 魏一鹤
 * @createDate: 2022-02-14 22:33
 **/
//多线程操作List
//并发下的集合都会出这个错  java.util.ConcurrentModificationException 并发修改异常
public class ListUnSafe2 {
public static void main(String[] args){
//初始化list 并发下list是不安全的
       ArrayList<String> arrayList=new ArrayList<String>();
//循环添加一些字符串
        for (int i = 1; i <= 10; i++) {
//10个线程去一起并发添加
            new Thread(()->{
//uuid随机生成的字符串并且截取前五位字符串
                arrayList.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}

报错

[null, a37dc, 96275]

[null, a37dc, 96275, e061d, 5d2c7, 80856, 2c464, 72ba5, 29bd1, 2ea0a]

[null, a37dc, 96275, e061d, 5d2c7]

[null, a37dc, 96275, e061d, 5d2c7, 80856, 2c464, 72ba5]

[null, a37dc, 96275, e061d, 5d2c7, 80856, 2c464]

Exception in thread "Thread-7" [null, a37dc, 96275, e061d]

[null, a37dc, 96275, e061d, 5d2c7, 80856]

[null, a37dc, 96275]

[null, a37dc, 96275]

java.util.ConcurrentModificationException

       at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)

       at java.util.ArrayList$Itr.next(ArrayList.java:861)

       at java.util.AbstractCollection.toString(AbstractCollection.java:461)

       at java.lang.String.valueOf(String.java:2994)

       at java.io.PrintStream.println(PrintStream.java:821)

       at com.wyh.unSafe.ListUnSafe2.lambda$main$0(ListUnSafe2.java:28)

       at java.lang.Thread.run(Thread.java:748)



注意这个异常 并发下的集合都会出这个错  java.util.ConcurrentModificationException:并发修改异常

那么既然List并发不安全,我们应该如何解决呢


  1. 使用Vector
package com.wyh.unSafe;
import java.util.*;
/**
 * @program: JUC
 * @description: 不安全的List 多线程
 * @author: 魏一鹤
 * @createDate: 2022-02-14 22:33
 **/
//多线程操作List
//并发下的集合都会出这个错  java.util.ConcurrentModificationException 并发修改异常
public class ListUnSafe2 {
public static void main(String[] args){
/**
         * 那么既然List并发不安全,我们应该如何解决呢
         *  1 把ArrayList换成Vector Vector默认就是安全的(不建议使用 因为涉及内存问题)
         *  
        **/
//初始化list 并发下list是不安全的 但是Vector默认就是安全的
        List<String> arrayList = new Vector<>();
//循环添加一些字符串
        for (int i = 1; i <= 10; i++) {
//10个线程去一起并发添加
            new Thread(()->{
//uuid随机生成的字符串并且截取前五位字符串
                arrayList.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}

[4a075, 9cf3d, 0d593]

[4a075, 9cf3d, 0d593, 1a746, 180e5, c0761, ebf2c, eadb4, a43e2, fc728]

[4a075, 9cf3d, 0d593, 1a746, 180e5, c0761, ebf2c, eadb4, a43e2, fc728]

[4a075, 9cf3d, 0d593, 1a746, 180e5, c0761, ebf2c, eadb4, a43e2, fc728]

[4a075, 9cf3d, 0d593, 1a746, 180e5, c0761, ebf2c, eadb4, a43e2]

[4a075, 9cf3d, 0d593, 1a746, 180e5, c0761, ebf2c, eadb4]

[4a075, 9cf3d, 0d593, 1a746, 180e5, c0761, ebf2c]


  1. 利用Collection工具类来转换安全的List 不止可以生成线程安全的List 还可以生成线程安全的Map Set

[4a075, 9cf3d, 0d593, 1a746, 180e5, c0761]

[4a075, 9cf3d, 0d593, 1a746, 180e5]

[4a075, 9cf3d, 0d593, 1a746]


package com.wyh.unSafe;
import java.util.*;
/**
 * @program: JUC
 * @description: 不安全的List 多线程
 * @author: 魏一鹤
 * @createDate: 2022-02-14 22:33
 **/
//多线程操作List
//并发下的集合都会出这个错  java.util.ConcurrentModificationException 并发修改异常
public class ListUnSafe2 {
public static void main(String[] args){
/**
         * 那么既然List并发不安全,我们应该如何解决呢
         *  1 把ArrayList换成Vector Vector默认就是安全的(不建议使用 因为涉及内存问题)
         *  2 利用Collection工具类来转换安全的List 不止可以生成线程安全的List 还可以生成线程安全的Map Set
         **/
        //使用Collections的synchronizedList方法返回一个新的线程安全带的List
        List<Object> arrayList = Collections.synchronizedList(new ArrayList<>());
//循环添加一些字符串
        for (int i = 1; i <= 10; i++) {
//10个线程去一起并发添加
            new Thread(()->{
//uuid随机生成的字符串并且截取前五位字符串
                arrayList.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}

[de36a, d1853, 1ffee]

[de36a, d1853, 1ffee, 381b9, 838fe, 10cf0, 42b85, 68cce, 1a592]

[de36a, d1853, 1ffee, 381b9, 838fe, 10cf0, 42b85, 68cce]

[de36a, d1853, 1ffee, 381b9, 838fe, 10cf0, 42b85]

[de36a, d1853, 1ffee, 381b9]

[de36a, d1853, 1ffee, 381b9, 838fe, 10cf0]

[de36a, d1853, 1ffee, 381b9, 838fe]

[de36a, d1853, 1ffee, 381b9]

[de36a, d1853, 1ffee]

[de36a, d1853, 1ffee, 381b9, 838fe, 10cf0, 42b85, 68cce, 1a592, 21ee9]



  1. 以上两种方法属于普通层面

以下这种属于JUC的,推荐使用JUC下的CopyOnWritrArrayList


package com.wyh.unSafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
 * @program: JUC
 * @description: 不安全的List 多线程
 * @author: 魏一鹤
 * @createDate: 2022-02-14 22:33
 **/
//多线程操作List
//并发下的集合都会出这个错  java.util.ConcurrentModificationException 并发修改异常
public class ListUnSafe2 {
public static void main(String[] args){
/**
         * 那么既然List并发不安全,我们应该如何解决呢
         *  1 把ArrayList换成Vector Vector默认就是安全的(不建议使用 因为涉及内存问题)
         *  2 利用Collection工具类来转换安全的List 不止可以生成线程安全的List 还可以生成线程安全的Map Set
         *  3 使用JUC下的CopyOnWriteArrayList 当然也有线程安全的CopyOnWriteArraySet
         **/
        //使用JUC下的CopyOnWriteArrayLis  它是一个线程安全的List
        //CopyOnWrite:写入时复制 又叫COW(CopyOnWrite)思想  它是计算机设计领域的一种优化策略
        //在写入的时候避免覆盖,造成数据问题  读写分离  写入的时候先复制出来,写入完之后再插入进去 保证线程安全
        List<Object> arrayList =new CopyOnWriteArrayList<>();
//循环添加一些字符串
        for (int i = 1; i <= 10; i++) {
//10个线程去一起并发添加
            new Thread(()->{
//uuid随机生成的字符串并且截取前五位字符串
                arrayList.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}


[3837d, 70019, bf585, 4d62a, 7575d]

[3837d, 70019, bf585, 4d62a, 7575d, a4ae9, 34025, 3b3d0]

[3837d, 70019, bf585, 4d62a, 7575d, a4ae9, 34025]

[3837d, 70019, bf585, 4d62a]

[3837d, 70019, bf585, 4d62a, 7575d, a4ae9]

[3837d, 70019, bf585, 4d62a]

[3837d, 70019, bf585, 4d62a]

[3837d, 70019, bf585, 4d62a, 7575d, a4ae9, 34025, 3b3d0]

[3837d, 70019, bf585, 4d62a, 7575d, a4ae9, 34025, 3b3d0, 5edaa, 45311]

[3837d, 70019, bf585, 4d62a, 7575d, a4ae9, 34025, 3b3d0, 5edaa]


那么为什么在并发的情况下使用CopyOnWritrArrayList就线程安全呢?

通过查看源码发现它也是使用一个数组,不过这个数组是被transient(有序的)和volatile(唯一的 )进行修饰的


image.png



目录
打赏
0
0
0
0
3
分享
相关文章
kde
|
10天前
|
Docker镜像加速指南:手把手教你配置国内镜像源
配置国内镜像源可大幅提升 Docker 拉取速度,解决访问 Docker Hub 缓慢问题。本文详解 Linux、Docker Desktop 配置方法,并提供测速对比与常见问题解答,附最新可用镜像源列表,助力高效开发部署。
kde
6635 15
|
7天前
typora免费版,激活方法,Typora使用教程
Typora是一款简洁高效的Markdown编辑器,支持即时渲染。本教程涵盖安装方法、文件操作、视图控制、格式排版、字体样式及Markdown语法,助你快速上手使用Typora进行高效写作。
1710 1
Dify MCP 保姆级教程来了!
大语言模型,例如 DeepSeek,如果不能联网、不能操作外部工具,只能是聊天机器人。除了聊天没什么可做的。
1661 27
国内如何安装和使用 Claude Code镜像教程 - Windows 用户篇
国内如何安装和使用 Claude Code镜像教程 - Windows 用户篇
1011 5
【保姆级图文详解】大模型、Spring AI编程调用大模型
【保姆级图文详解】大模型、Spring AI编程调用大模型
674 9
【保姆级图文详解】大模型、Spring AI编程调用大模型
2025年最新版最细致Maven安装与配置指南(任何版本都可以依据本文章配置)
本文详细介绍了Maven的项目管理工具特性、安装步骤和配置方法。主要内容包括: Maven概述:解释Maven作为基于POM的构建工具,具备依赖管理、构建生命周期和仓库管理等功能。 安装步骤: 从官网下载最新版本 解压到指定目录 创建本地仓库文件夹 关键配置: 修改settings.xml文件 配置阿里云和清华大学镜像仓库以加速依赖下载 设置本地仓库路径 附加说明:包含详细的配置示例和截图指导,适用于各种操作系统环境。 本文提供了完整的Maven安装和配置
2025年最新版最细致Maven安装与配置指南(任何版本都可以依据本文章配置)
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等