04_并发容器类

news2025/9/17 1:35:12

1. 重现线程不安全:List

首先以List作为演示对象,创建多个线程对List接口的常用实现类ArrayList进行add操作。

public class NotSafeDemo {

    public static void main(String[] args) {

        List<String> list = new ArrayList<>();

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

测试结果:  

出现了线程不安全错误

ArrayList在多个线程同时对其进行修改的时候,就会抛出java.util.ConcurrentModificationException异常(并发修改异常),因为ArrayList的add及其他方法都是线程不安全的,有源码佐证:

解决方案:

​ List接口有很多实现类,除了常用的ArrayList之外,还有VectorSynchronizedList

他们都有synchronized关键字,说明都是线程安全的。

 

改用Vector或者synchronizedList试试:即可解决!

public static void main(String[] args) {

        //List<String> list = new Vector<>();
        List<String> list = Collections.synchronizedList(new ArrayList<>());

        for (int i = 0; i < 200; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }

Vector和Synchronized的缺点:

vector:内存消耗比较大,适合一次增量比较大的情况

SynchronizedList:迭代器涉及的代码没有加上线程同步代码  

2. CopyOnWrite容器

什么是CopyOnWrite容器?

CopyOnWrite容器(简称COW容器)即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器

从JDK1.5开始Java并发包里提供了两个使用CopyOnWrite机制实现的并发容器,它们是CopyOnWriteArrayListCopyOnWriteArraySet

先看看CopyOnWriteArrayList类:发现它的本质就是数组

再来看看CopyOnWriteArrayList的add方法:发现该方法是线程安全的

使用CopyOnWriteArrayList改造main方法:  

    public static void main(String[] args) {

        //List<String> list = new Vector<>();
        //List<String> list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();

        for (int i = 0; i < 200; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }

CopyOnWrite并发容器用于读多写少的并发场景。比如:白名单,黑名单。假如我们有一个搜索网站,用户在这个网站的搜索框中,输入关键字搜索内容,但是某些关键字不允许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中,黑名单一定周期才会更新一次。

缺点:

  1. 内存占用问题。写的时候会创建新对象添加到新容器里,而旧容器的对象还在使用,所以有两份对象内存。通过压缩容器中的元素的方法来减少大对象的内存消耗,比如,如果元素全是10进制的数字,可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器,而使用其他的并发容器,如ConcurrentHashMap。

  2. 数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。

3. 扩展类比:Set和Map

HashSet和HashMap也都是线程不安全的,类似于ArrayList,也可以通过代码证明。

private static void notSafeMap() {
        Map<String, String> map = new HashMap<>();

        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(String.valueOf(Thread.currentThread().getName()), UUID.randomUUID().toString().substring(0, 8));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }

    private static void notSafeSet() {
        Set<String> set = new HashSet<>();

        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }

都会报:ConcurrentModificationException异常信息。

Collections提供了方法synchronizedList保证list是同步线程安全的,Set和Map呢?

HashMap<String, String> map = new HashMap<>(); //不安全
Hashtable<String, String> map = new Hashtable<>(); //安全
Map<String, String> map = Collections.synchronizedMap(new HashMap<>()); //安全
ConcurrentMap<String, String> map = new ConcurrentMap<>(); //安全

JUC提供的CopyOnWrite容器实现类有:CopyOnWriteArrayList和CopyOnWriteArraySet。

有没有Map的实现:

最终实现:

public class NotSafeDemo {

    public static void main(String[] args) {
        notSafeList();
        notSafeSet();
        notSafeMap();
    }

    private static void notSafeMap() {
        //Map<String, String> map = new HashMap<>();
        //Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, String> map = new ConcurrentHashMap<>();

        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                map.put(String.valueOf(Thread.currentThread().getName()), UUID.randomUUID().toString().substring(0, 8));
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }

    private static void notSafeSet() {
        //Set<String> set = new HashSet<>();
        Set<String> set = new CopyOnWriteArraySet<>();

        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set);
            }, String.valueOf(i)).start();
        }
    }

    private static void notSafeList() {
        //List<String> list = new Vector<>();
        //List<String> list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(list);
            }, String.valueOf(i)).start();
        }
    }
}

扩展:HashSet底层数据结构是什么?HashMap ?

但HashSet的add是放一个值,而HashMap是放K、V键值对

4. 并发容器和同步容器

同步容器可以简单地理解为通过synchronized来实现同步的容器。同步容器会导致多个线程中对容器方法调用的串行执行,降低并发性,因为它们都是以容器自身对象为锁。在并发下进行迭代的读和写时并不是线程安全的。如:Vector、Stack、HashTable、Collections类的静态工厂方法创建的类(如Collections.synchronizedList)

并发容器是针对多个线程并发访问而设计的,在jdk5.0引入了concurrent包,其中提供了很多并发容器,如ConcurrentHashMap、CopyOnWriteArrayList等。

ConcurrentHashMap:内部采用Segment结构,进行两次Hash进行定位,写时只对Segment加锁
CopyOnWriteArrayList:CopyOnWrite写时复制一份新的,在新的上面修改,然后把引用指向新的。只能实现数据的最终一致性,非实时一致的;代替List,适用于读操作为主的情况

同步容器并发容器都为多线程并发访问提供了合适的线程安全,不过并发容器的可扩展性更高。

public static void main(String[]args){
    Vector v = new Vector();
    for (int i = 0; i < 10; i++) {
        int a = i;
        new Thread(()->{
            v.add(a);
        }).start();
    }
    for (Iterator iterator = v.iterator(); iterator.hasNext();) {
        int element = (int) iterator.next();
        v.remove(element);
    }
}

5. 性能测试(了解)

① 引入spring-core依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.3.18</version>
</dependency>

② 测试代码

StopWatch stopwatch = new StopWatch();

public static void main(String[] args) {
    StopWatch stopwatch = new StopWatch();
    List<Integer> list = new Vector<>();
    stopwatch.start("Vector:write数据");
    IntStream.rangeClosed(1,1000000).parallel().forEach( a ->{
        list.add(new Random().nextInt(1000000));
    });
    stopwatch.stop();
    stopwatch.start("Vector:read数据");
    IntStream.rangeClosed(1,1000000).parallel().forEach( a ->{
        list.get(new Random().nextInt(1000000));
    });
    stopwatch.stop();
    System.out.println(stopwatch.prettyPrint());
}

③ 结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/468150.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

keil移植linux(makefile)

文章目录 运行环境&#xff1a;1.1 freeRTOS_LED工程移植1)修改cubeMX配置2)setting设置3)launch设置4)修改makefile5)修改代码6)实验效果 运行环境&#xff1a; ubuntu18.04.melodic 宏基暗影骑士笔记本 stm32f427IIH6 stlink 9-24v可调电源 robomaster A 板 1.1 freeRTOS_L…

Kubernetes集群的类似top的查看指标的工具ktop

Kubernetes集群的类似top的工具。 遵循Unix/Linux顶级工具的传统&#xff0c;ktop是一个显示有关Kubernetes集群中运行的节点、pod和其他工作负载资源的有用度量信息的工具。 项目地址&#xff1a;https://github.com/vladimirvivien/ktop使用效果图&#xff1a; 特性 集群资…

Linux如何压缩和解压文件

先看压缩 Linux zip 命令用于压缩文件。 zip 是个使用广泛的压缩程序&#xff0c;压缩后的文件后缀名为 .zip 将 /home/html/ 这个目录下所有文件和文件夹打包为当前目录下的 html.zip&#xff1a; zip -r html.zip /home/html 如果在我们在 /home/html 目录下&#xff0c;可…

经济回暖、兴趣电商升级,品牌在竞争白热化的市场中如何突围?| D3大会圆桌回顾

冬去春来&#xff0c;消费市场韧性回弹&#xff0c;消费趋势正处于“转折”和“跃升”的阶段。新的机遇和挑战也将伴随着新的思维、方法和模式&#xff0c;呈现出更多元的变化和创新&#xff1a;渠道虚实融合&#xff0c;内容为王&#xff0c;社会化媒体成为主战场等消费场景不…

13-NumPy

文章目录 一.基础1.Ndarray对象2.数据类型 二.数组1.数组属性&#xff08;1&#xff09;arange&#xff08;2&#xff09;shape&#xff08;3&#xff09;ndim&#xff08;4&#xff09;itemsize 2.创建数组&#xff08;1&#xff09;empty&#xff08;2&#xff09;zero&#…

【接口自动化测试】月薪12k必会技术,从0到1学习接口自动化测试,6个操作安排的明明白白

导读&#xff1a;在所有的开发测试中&#xff0c;接口测试是必不可少的一项。有效且覆盖完整的接口测试&#xff0c;不仅能保障新功能的开发质量&#xff0c;还能让开发在修改功能逻辑的时候有回归的能力&#xff0c;同时也是能优雅地进行重构的前提。编写接口测试要遵守哪些原…

电商直播商家崛起所面临的问题,订单管理系统起关键性作用

随着短视频直播的兴起&#xff0c;电商企业再次迎来大爆发&#xff0c;随着销量的猛增&#xff0c;随之而来的的订单处理成了各大商家的头等大事&#xff0c;面对再次崛起的电商蓝库云认为企业在订单管理会经常遇到以下问题&#xff1a; 电商企业在订单管理中可能面临以下问题…

js版计算连续12个月计算不超3万公里

<!--考虑比亚迪车友不是程序员的多&#xff0c;写了个html版的&#xff0c;复制以下代码在记事本&#xff0c;改后缀名为test.html&#xff0c;然后用浏览器打开--> <!DOCTYPE html> <html><head><meta charset"utf-8" /><title>…

基于SpringBoot3从零配置MybatisPlus

基于SpringBoot3从零配置MybatisPlus记录 文章目录 1.环境2.表数据准备3. 配置pom配置yml 配置MapperScan 3.问题总结问题1: Property sqlSessionFactory or sqlSessionTemplate are required问题2&#xff1a;org.apache.ibatis.binding.BindingException: Invalid bound stat…

Python学习13:说句心里话 A(python123)

描述 分两次从控制台接收用户的两个输入&#xff1a;第一个内容为"人名"&#xff0c;第二个内容为"心里话"。‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪…

使用D435i深度相机运行ORB-SLAM3

下载安装链接 下载ORB-SLAM3地址&#xff1a; git clone https://github.com/UZ-SLAMLab/ORB_SLAM3.git eigen3多版本安装&#xff1a;https://blog.csdn.net/weixin_41756645/article/details/129570141 ORB-SLAM2中eigen3版本为&#xff1a;3.2.10版本 ORB-SLAM3中eigen3版…

【Jetpack】ViewModel + LiveData + DataBinding 综合使用 ( 核心要点说明 | 组合方式 | 代码示例 )

文章目录 一、ViewModel LiveData DataBinding 核心要点1、ViewModel 使用要点2、LiveData 使用要点3、DataBinding 使用要点 二、ViewModel LiveData DataBinding 代码示例1、ViewModel LiveData 代码2、build.gradle 构建脚本 - 启用 DataBinding3、DataBinding 布局文件…

ChatGPT实战100例 - (10) 提前体验ChatGPT的多模态绘图功能

文章目录 ChatGPT实战100例 - (10) 提前体验ChatGPT的多模态绘图功能一、需求与思路二、基本调教三、开始秀四、 总结 ChatGPT实战100例 - (10) 提前体验ChatGPT的多模态绘图功能 这个绘图其实比较基础&#xff0c;只能说是能显示个图吧 真要出图&#xff0c;隔壁 文心一言 秒杀…

如何提高三维模型OSGB格式转换3DTILES的转换速度和数据质量

如何提高三维模型OSGB格式转换3DTILES的转换速度和数据质量 提高三维模型从OSGB格式转换为3DTILES格式的转换速度和数据质量&#xff0c;可以从以下几个方面进行优化&#xff1a; 1、选用高效的转换工具&#xff1a;选择高效的转换工具是提高转换速度和数据质量的关键。目前市…

.NET中比肩System.Text.Json序列化反序列化组件MessagePack

简介 官方定义&#xff1a;MessagePack是一种高效的二进制序列化格式。它允许您像JSON一样在多个语言之间交换数据。但是它更快并且更小。 MessagePack是一种开源的序列化反序列化组件&#xff0c;可支持JAVA&#xff0c;C#等主流语言。在 C# 中使用 MessagePack&#xff0c…

javaScript:cropperjs是一款非常强大却又简单的图片裁剪工具

cropperjs是一款非常强大却又简单的图片裁剪工具&#xff0c;它可以进行非常灵活的配置&#xff0c;支持手机端使用&#xff0c;支持包括IE9以上的现代浏览器。&#xff08;关键是使用方法简单&#xff0c;几行代码就可以搞定&#xff09; 官方github文档&#xff1a;GitHub -…

深入理解计算机系统--理解编译器编译的过程

前言 大家在学习C语言的时候&#xff0c;相信对编译器这个词并不会感到陌生。我们也会知道编译器编译的过程是&#xff1a;预处理-》编译-》汇编-》链接。这篇文章主要介绍这四个过程中&#xff0c;编译器究竟做了那些工作&#xff0c;它是如何让一份高级程序转换成机器语言的…

USB 转 4 串口芯片 CH9104

CH9104 是一款USB总线的转接芯片&#xff0c;支持最高6M波特率与硬件流控&#xff0c;支持USB配置功能&#xff0c;提供RS485方向控制与GPIO等信号引脚&#xff0c;可实现PC等平台扩展多串口或多个串口设备升级成USB口。CH9104实现 USB 转四个异步串口 UART0/1/2/3 功能&#x…

Docker 在Linux-CentOS上的安装使用

Linux CentOS 虚拟机安装及与windows远程登录_XiaoGuaiSs的博客-CSDN博客 一、如果安装过程有兼容问题&#xff0c;执行更新 yum 包&#xff08;生产环境中此步操作需慎重&#xff09; 注意​ yum -y update&#xff1a;升级所有包同时也升级软件和系统内核&#xff1b;​ …

计讯物联智能虫情测报系统,保障粮食安全的守卫者

计讯物联农业虫情监测系统 夏天是粮食作物生长关键期&#xff0c;也是农作物病虫害高发期。因此&#xff0c;预防农作物病虫害在生产过程中是至关重要的环节。作为智慧农业的先行者&#xff0c;计讯物联以预防为主、综合防治为设计理念&#xff0c;利用先进的现代光、电、数控技…