【不安全的集合类】同步容器(如ConcurrentHashMap)、并发集合(如CopyOnWriteArrayList)

本文主要是介绍【不安全的集合类】同步容器(如ConcurrentHashMap)、并发集合(如CopyOnWriteArrayList),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 一、List的线程不安全
    • 二、Set的线程不安全
    • 三、Map的线程不安全

日常我们用到的集合的情况会很多,在单线程的情况下,不用考虑到线程安全的问题,但是如果在多线程开发的过程中,我们该选择哪一种类型来保证线程安全性呢

一、List的线程不安全

我们先来看一个例子:

package com.atguigu.signcenter.nosafe;import java.util.ArrayList;
import java.util.UUID;/*** 题目:请举例说明集合类是不安全的* @author: jd* @create: 2024-09-02*/
public class NotSafeDemo {public static void main(String[] args) {ArrayList<String> StrList = new ArrayList<>();for (int i = 0; i <=30 ; i++) {new Thread(()->{StrList.add(UUID.randomUUID().toString().substring(0,8));System.out.println("StrList = " + StrList);},String.valueOf(i)).start();}}}

结果:从图中可以看出来,执行一段时间之后发生了错误;这个错误就是多线程任务中对同一个集合处理过程中出现了冲突的情况导致的
在这里插入图片描述
导致原因&解决方案
会出现ConcurrentModificationException是因为ArrayList的add方法不是线程安全的;当某个线程正在向List中写入数据时,另外一个线程同时进来写入,就会导致ConcurrentModificationException。

  1. 我们可以使用线程安全的集合类Vector,其add方法是同步方法(保证了数据一致性,但是访问性能下降);
  2. 使用Collections.synchronizedList(new ArrayList<>()) 创建一个线程安全的List (其实就是在add的时候使用了synchronized同步代码块);
  3. CopyOnWriteArrayList 写时复制ArrayList (多线程建议使用这个)
    Vector是线程安全的,能够保证数据一致性但是性能低;ArrayList牺牲了数据一致性提升了读写效率;现在想要保证数据一致性的同时也要保证读写效率,那应该怎么办?因此出现了读写分离的CopyOnWriteArrayList 。

CopyOnWriteArrayList的写时复制
我们可以先看看CopyOnWriteArrayList中add方法的源码

private transient volatile Object[] array;public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();   // 获取当前List中的所有元素int len = elements.length;  Object[] newElements = Arrays.copyOf(elements, len + 1);  // 拷贝旧元素到新的扩容的数组中newElements[len] = e;  // 添加新的值setArray(newElements);  // 更新return true;} finally {lock.unlock();}
}

CopyOnWrite容器即写时复制的容器,往一个容器中添加元素的时候,不直接往当前容器Object[]添加,而是现将当前容器Object[]进行Copy,而是复制出一个新的容器Object[] newElements向新容器添加元素,添加之后,再将原容器的引用指向新的容器setArray(newElements);这样做的好处时可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。同时添加元素的过程是通过ReentrantLock 锁来实现了同一时间内只有一个线程对此方法的访问。

二、Set的线程不安全

public class NotSafeDemo {public static void main(String[] args) {Set<String> set = new HashSet<>();for (int i = 1; i <= 30; i++) {new Thread(() -> {set.add(UUID.randomUUID().toString().substring(0, 8));System.out.println(set);}, String.valueOf(i)).start();}}
}

面这段代码同样会出现java.util.ConcurrentModificationException异常;同样可以使用Collections.synchronizedSet()和CopyOnWriteArraySet,其具体原理与List的一致。这里浅说一下HashSet的源码,HashSet其实就是一个HashMap,HashSet的中存的值是HashMap的key,HashMap中的Value是一个固定对象PRESENT。

三、Map的线程不安全

同样HashMap也是线程不安全的,可以使用集合工具类Collections.synchronizedMap(new HashMap<String, String>())和ConcurrentHashMap创建线程安全的HashMap

public class NotSafeDemo {public static void main(String[] args) {
//        HashMap<String, String> map = new HashMap<>();
//        HashMap<String, String> map1 = (HashMap<String, String>) Collections.synchronizedMap(new HashMap<String, String>());ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();for (int i = 1; i <= 30; i++) {new Thread(() -> {map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));System.out.println(map);}, String.valueOf(i)).start();}}
}

结果正确且正常结束
在这里插入图片描述

这篇关于【不安全的集合类】同步容器(如ConcurrentHashMap)、并发集合(如CopyOnWriteArrayList)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132058

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Java集合之Iterator迭代器实现代码解析

《Java集合之Iterator迭代器实现代码解析》迭代器Iterator是Java集合框架中的一个核心接口,位于java.util包下,它定义了一种标准的元素访问机制,为各种集合类型提供了一种统一的... 目录一、什么是Iterator二、Iterator的核心方法三、基本使用示例四、Iterator的工

Java JUC并发集合详解之线程安全容器完全攻略

《JavaJUC并发集合详解之线程安全容器完全攻略》Java通过java.util.concurrent(JUC)包提供了一整套线程安全的并发容器,它们不仅是简单的同步包装,更是基于精妙并发算法构建... 目录一、为什么需要JUC并发集合?二、核心并发集合分类与详解三、选型指南:如何选择合适的并发容器?在多

Java 结构化并发Structured Concurrency实践举例

《Java结构化并发StructuredConcurrency实践举例》Java21结构化并发通过作用域和任务句柄统一管理并发生命周期,解决线程泄漏与任务追踪问题,提升代码安全性和可观测性,其核心... 目录一、结构化并发的核心概念与设计目标二、结构化并发的核心组件(一)作用域(Scopes)(二)任务句柄

python语言中的常用容器(集合)示例详解

《python语言中的常用容器(集合)示例详解》Python集合是一种无序且不重复的数据容器,它可以存储任意类型的对象,包括数字、字符串、元组等,下面:本文主要介绍python语言中常用容器(集合... 目录1.核心内置容器1. 列表2. 元组3. 集合4. 冻结集合5. 字典2.collections模块

Spring Boot中获取IOC容器的多种方式

《SpringBoot中获取IOC容器的多种方式》本文主要介绍了SpringBoot中获取IOC容器的多种方式,包括直接注入、实现ApplicationContextAware接口、通过Spring... 目录1. 直接注入ApplicationContext2. 实现ApplicationContextA

linux配置podman阿里云容器镜像加速器详解

《linux配置podman阿里云容器镜像加速器详解》本文指导如何配置Podman使用阿里云容器镜像加速器:登录阿里云获取专属加速地址,修改Podman配置文件并移除https://前缀,最后拉取镜像... 目录1.下载podman2.获取阿里云个人容器镜像加速器地址3.更改podman配置文件4.使用po

k8s容器放开锁内存限制问题

《k8s容器放开锁内存限制问题》nccl-test容器运行mpirun时因NCCL_BUFFSIZE过大导致OOM,需通过修改docker服务配置文件,将LimitMEMLOCK设为infinity并... 目录问题问题确认放开容器max locked memory限制总结参考:https://Access

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建