当前位置: 首页 > news >正文

多线程——线程安全的集合类

目录

·前言

一、多线程环境使用 ArrayList

1.进行加锁

2.使用 SynchronizedList 类

3.使用 CopyOnWriteArrayList 类

二、多线程环境使用队列

1.进行加锁

2.使用阻塞队列

三、多线程环境使用哈希表

1.Hashtable

2.ConcurrentHashMap

(1)缩小锁粒度

(2)使用 CAS 机制

(3)优化扩容操作

3.Hashtable、HashMap 与 ConcurrentHashMap 的区别

·结尾


·前言

        介绍了这么多关于多线程相关的知识后,在我们 Java 中哪些集合类在使用的时候是线程安全的呢?其实,我们了解的集合类大部分都是线程不安全的,像 HashMap、ArrayList……,那么本篇文章就来介绍一下哪些集合类是线程安全的,它们又是如何保证线程安全的。

一、多线程环境使用 ArrayList

1.进行加锁

        为了保证在多线程环境下,对 ArrayList 进行操作是线程安全的,我们可以在调用 ArrayList 的相关方法时都进行加锁操作,使用 synchronized 或者使用 ReentrantLock,这样就可以保证线程的安全。

2.使用 SynchronizedList 类

        使用这种方式创建 ArrayList 方式如下代码所示:

List list = Collections.synchronizedList(new ArrayList());

        这种方式可以认为是给 ArrayList 套了一个壳,原本 ArrayList 各种操作都是不带锁的,通过上述套壳操作后,得到了新的对象,在新的对象中,里面的方法就都是带有锁的。

        使用这种方式可以把 List 所以的子类都转成线程安全的类,关于这种方式的详细用法与介绍可以参考一下:Collections.synchronizedList使用 - hongdada - 博客园 这篇文章,里面的讲解十分详细,我在这里就不展开进行介绍啦。

3.使用 CopyOnWriteArrayList 类

        CopyOnWriteArrayList 类保证线程安全的做法是用到了写时拷贝,在多线程环境下使用 ArrayList 遇见的线程安全问题大多都是涉及到多个线程对同一个 ArrayList 进行修改操作,使用 CopyOnWriteArrayList 在面对多个线程修改时,进行的操作就是把整个顺序表复制一份,修改新的顺序表中的内容,修改完成后再修改引用的指向,使引用指向新的顺序表,用这种方式来保证线程安全,这个过程如下图所示:

        最后修改引用指向的操作是原子的所以不加锁也不会有问题。

        上述的这种操作也是存在一定的局限性的,比如出现一下两种情况:

  1. 对 List 涉及到频繁的修改操作;
  2. 顺序表非常大; 

        此时使用 CopyOnWriteArrayList ,写时拷贝就会产生很大的开销,如果这种开销比加锁的开销还要大,那就得不偿失了。

二、多线程环境使用队列

1.进行加锁

        想要在多线程环境中使用队列保证线程安全,那么我们可以在调用队列相关方法时都进行加锁的操作,使用 synchronized 或者使用 ReentrantLock,这样就可以保证线程的安全。

2.使用阻塞队列

        使用 BlockingQueue 实现的阻塞队列,要注意,阻塞队列不仅有阻塞的作用,还有保证线程安全的作用,关于阻塞队列的介绍可以参考我前面的文章:多线程——阻塞队列_php 队列阻塞问题-CSDN博客 这里我对阻塞队列进行了详细的介绍,已经实现一个简单的阻塞队列。

三、多线程环境使用哈希表

        在多线程中使用 HashMap 本身不是线程安全的,想要在多线程环境下使用哈希表就可以使用:Hashtable 或者 ConcurrentHashMap。

1.Hashtable

        Hashtable 这里保证线程安全的方式很简单,如下图所示:

        进入 Hashtable 源码可以发现,Hashtable 这里保证线程安全的方式就是把关键的方法都加上 synchronized 关键字,前面介绍过,对方法加 synchronized 进行修饰,等同于直接针对 Hashtable 对象本身进行加锁,这时就会出现下图的情况:        上图两个线程是想针对不同的链表进行修改操作,可是由于 Hashtable 中 synchronized 直接对 Hashtable 对象本身进行了加锁,所以即使是修改不同的链表也会出现锁冲突。

        我们可以仔细观察一下上图,如果是修改两个不同链表上的元素就不会涉及到线程安全的问题,这属于修改不同变量,但是如果是修改同一个链表上的元素就可能涉及到线程安全问题,此时针对不同链表的操作再进行加锁就产生了多余的锁冲突了。

        下面我来总结一下 Hashtable 的缺点,有以下几条:

  1. 如果多个线程访问同一个 Hashtable 就会直接发送锁冲突,这时就会产生一些多余的锁冲突,造成代码在多线程执行中效率低下;
  2. size 方法也是使用 synchronized 进行修饰的,操作起来就会比较慢;
  3. Hashtable 一旦涉及到扩容操作,就由该线程完成整个扩容操作,这个过程中涉及到大量的元素拷贝,效率会非常低。 

2.ConcurrentHashMap

        ConcurrentHashMap 相比与 Hashtable 做出了一系列的改进和优化,下面我来介绍一下,ConcurrentHashMap 都做了些什么优化。

(1)缩小锁粒度

        为了优化 Hashtable 中对不同链表操作出现的多余锁冲突,在 ConcurrentHashMap 中的加锁方式是对每条链表都分配一个单独的锁,如下图所示:

        此时,t1 线程与 t2 线程在修改不同链表的时候就不会出现锁冲突,并且,这里虽然是对每条链表都分配了把锁,但是并没有产生更多的空间代价,这是因为 Java 中任何一个对象都可以作为锁对象,在哈希表中,本身就要有数组,数组中每个元素都是已经存在的(每个链表的头节点),此时,只要使用数组元素(链表头节点)作为加锁的对象即可完成对每条链表都分配锁的操作。

        在 Java 1.7 及以前,ConcurrentHashMap 是通过“分段锁”来实现的,就是给若干个链表分配一把锁,这种设定实现更复杂,效率也不高,还会引入额外的空间开销,所以从  Java 1.8 开始,ConcurrentHashMap 就设定成每个链表一把锁了。

(2)使用 CAS 机制

        在 ConcurrentHashMap 中充分使用了 CAS 机制进行原子操作,减少了部分加锁,比如:在针对整个哈希表元素的个数维护。

(3)优化扩容操作

        扩容对于哈希表来说永远都是一个重量级的操作,在 HashMap 中有一个负载因子,它用来描述每个链表上平均有多少元素,为了保证哈希表的查找效率,每个链表上的元素个数不应该太长,如果太长就会涉及到以下两种解决方案:

  1. 面对个别链表长度过长,会将该链表变成树状结构;
  2. 如果负载因子到达规定阈值,就会进行扩容操作。

        所谓的扩容操作就是创建一个更大的数组,把旧的哈希表中的元素都搬运(插入/删除)到新的数组上,如果哈希表本身元素有很多,这里的扩容操作就会消耗很长时间。

        那么 ConcurrentHashMap 是如何针对扩容操作进行的优化呢?这里使用的是化整为零的思路,以往的 HashMap 或者 Hashtable 面对扩容操作都是在某一次插入元素操作中将整体一次性完成扩容,而 ConcurrentHashMap 在面对扩容操作时是每次操作都只搬运一部分元素,这样确保每次操作消耗的时间都不会很长,就避免了出现很卡的情况了。

        在 ConcurrentHashMap 进行扩容的过程中会同时存两份哈希表,一份是旧的,一份是新的,此时进行的各操作流程为:

  • 插入操作:直接往新的哈希表中插;
  • 删除操作:新的哈希表与旧的哈希表中都直接删除;
  • 查找操作:在新的哈希表与旧的哈希表中都进行查询。 

        最后扩容完成后,把旧的哈希表给删除。 

3.Hashtable、HashMap 与 ConcurrentHashMap 的区别

  1. HashMap:线程不安全,key 允许为 null;
  2. Hashtable:线程安全,使用 synchronized 锁 Hashtable 对象,增加多余的所冲突,效率较低,key 不允许为 null;
  3. ConcurrentHashMap:线程安全,使用 synchronized 锁每个链表的头节点,锁冲突的概率降低,充分利用 CAS 机制,优化了扩容的方式,key 不允许为 null。

·结尾

        文章到此就要结束了,本篇文章介绍了一些线程安全的集合类,并且介绍了它们是如何保证线程安全的,希望看完本篇文章,让你在多线程编程的过程中会多注意使用的类是否存在线程安全问题,并且理解上述线程安全的集合类为保证线程安全所进行操作的思路,这会对我们在多线程环境下如何使用线程不安全的类也能写出线程安全的代码有所帮助,如果本篇文章对你有所帮助,希望能得到你的支持,如果对文章有什么不理解的地方欢迎在评论区进行留言,那么我们就下一篇文章再见咯~~~

相关文章:

多线程——线程安全的集合类

目录 前言 一、多线程环境使用 ArrayList 1.进行加锁 2.使用 SynchronizedList 类 3.使用 CopyOnWriteArrayList 类 二、多线程环境使用队列 1.进行加锁 2.使用阻塞队列 三、多线程环境使用哈希表 1.Hashtable 2.ConcurrentHashMap (1)缩小锁…...

自动化数据库管理:如何通过存储过程动态创建 MySQL 对象

在当今数据驱动的世界中,高效的数据库管理至关重要。本文将展示如何通过存储过程自动化地创建各种 MySQL 数据库对象,包括数据表、视图、字段、索引、约束、存储过程、定时器和事件。通过这些方法,我们可以快速响应业务需求,提高数…...

480p 720p 1080p 2k 4k 8k 12k分辨率视频分别占用多大带宽?

技术背景 好多开发者,在设置视频编码参数的时候,对不同分辨率的带宽设置,缺乏相关的经验,实际上,视频分辨率与所需带宽之间的关系受到多个因素的影响,包括视频编码方式、帧率、视频内容的动态程度等。下面…...

unity中GameObject介绍

在 Unity 中,Cube和Sphere等基本几何体是 Unity 引擎的内置预制体(Prefabs),它们属于 Unity 中的GameObject 系统,可以在 Unity 的 Hierarchy 视图或 Scene 视图中右键点击,然后在弹出的菜单中选择 3D Obje…...

洛谷——P8468 [Aya Round 1 C] 文文的构造游戏(01构造问题)

P8468 [Aya Round 1 C] 文文的构造游戏 题目描述 [Aya Round 1 C] 文文的构造游戏 - 洛谷 运行代码&#xff08;暴力枚举&#xff09;——超时 #include <stdio.h> #define ll long long const int N 1e6 5; // 计算数组元素的异或和 ll xorSum(ll arr[], int n) {l…...

双击热备和负载均衡的区别

区别&#xff1a; 双机热备 (heartbeat)&#xff1a;对同一应用来讲&#xff0c;永远是主机应用启动&#xff0c;备机应用停止的一主一备模式(两台通常叫双击热备&#xff0c;多台称为高可用) 负载均衡&#xff1a;两台/多台服务器 上同一个应用系统同时工作&#xff0c;分担负…...

如何使用 cPanel 部署 WordPress临时网站

对于依赖WordPress站点或WooCommerce商店的企业来说&#xff0c;在生产环境中直接修改站点风险很大。而WordPress的临时网站是一个更安全的选择&#xff0c;可以通过使用临时网站进行编辑来规避风险。 在本文中&#xff0c;我们将详细介绍WordPress临时网站的相关知识、使用临时…...

Android 自定义 Dialog 实现列表 单选,多选,搜索

前言 在Android开发中&#xff0c;通过对话框让用户选择&#xff0c;筛选信息是很方便也很常见的操作。本文详细介绍了如何使用自定义 Dialog、RecyclerView 以及自定义搜索框 来实现选中状态和用户交互&#xff0c;文中大本分代码都有明确注释&#xff0c;主打一个简单明了&a…...

下载地址合辑(持续更新)

下载地址合辑 汇总OSG相关地址Visual Studio Qt 地址qt插件安装失败 Boost库boost库编译步骤 FFmpeg 地址osg编译库 常用的下载地址&#xff1a; 汇总 vlc 地址&#xff1a; https://www.videolan.org/vlc/index.zh_CN.html visual 地址&#xff1a;https://my.visualstudio.…...

Android Kotlin 高阶函数详解及其在协程中的应用

文章目录 1. 引言2. 什么是高阶函数&#xff1f;3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda 表达式3.3 匿名函数3.4 返回函数 4. 高阶函数的深入用法4.1 函数组合4.2 内联函数4.3 高阶扩展函数 5. Kotlin 高阶函数的对比优势5.1 与 Java 的对比5.2 与 JavaScript 的…...

CSS基础—网页布局(重点!)

1、两列布局 &#xff08;1&#xff09;概念 经典两列布局是指一种网页布局方式&#xff0c;其中一列宽度固定&#xff0c;另一列宽度自适应。‌ 这种布局方式在网页设计中非常常见&#xff0c;因为它能够提供良好的视觉效果和用户体验。 如图所示&#xff1a; 页面顶部放置一…...

【Fargo】17:vs工程转qt构建:QT6 不支持32bit转向qt5.15.2

vs2022的console 工程加入qt支持后使用qt15.2 的vs2019 库,变为一个qt界面程序。最终效果 一些参考 qt5的项目搭建 qt5 最多支持到vs2019 qt6 最新 已经支持vs2022 国内还是以qt5.15为主 升级qt的vstools...

​智能电表蓝牙芯片方案

RAMSUN基于自研射频技术和基带算法提供蓝牙MCU。蓝牙MCU配套成熟的网络协议栈和丰富的示例代码及多平台APP工具。部分芯片型号无需二次开发&#xff0c;即连即用&#xff1b;提供特色蓝牙/串口/USB三通芯片&#xff0c;为更多复杂无线应用赋能。 应用案例说明: BLE方便用户直接…...

miRNA分析流程学习(一)/TCGAmiRNA数据下载

miRNA&#xff08;microRNA&#xff09; 是一种小的非编码 RNA 分子&#xff0c;通常由 20 到 24 个核苷酸组成。miRNA 主要存在于动植物中&#xff0c;并在基因表达调控中起到关键作用。它们通过与特定的信使 RNA&#xff08;mRNA&#xff09;分子结合来抑制基因表达&#xff…...

西南大学软件专硕考研难度分析!

C哥专业提供——计软考研院校选择分析专业课备考指南规划 西南大学软件工程学硕近三年呈现出招生规模稳定、复试线稳中有升的特点。2024届实际录取8人&#xff0c;复试分数线305分&#xff0c;复试录取率67%&#xff0c;相比去年复试线略有下降但仍高于2022届&#xff0c;显示出…...

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-21

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-21 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-21目录1. The Fair Language Model Paradox摘要研究背景问题与挑战如何解决创新点算法模型实验效果重要数据与结论推荐阅读指数&…...

安全芯片 OPTIGA TRUST M 使用介绍与示例(基于STM32裸机)

文章目录 目的资料索引硬件电路软件框架介绍数据存储框架移植框架使用 使用示例示例地址与硬件连接通讯测试功能测试 总结 目的 OPTIGA TRUST M 是英飞凌推出的安全芯片&#xff0c;芯片通提供了很多 slot &#xff0c;用于存放各类安全证书、密钥、用户数据等&#xff0c;内置…...

【AI换装整合及教程】CatVTON:时尚与科技的完美融合

在当今数字化时代&#xff0c;时尚行业正经历着一场前所未有的变革&#xff0c;而 CatVTON 作为一款由中山大学、Pixocial 等机构联合研发的轻量化 AI 虚拟换装工具&#xff0c;无疑是这场变革中的璀璨明星。 一、独特的技术架构 CatVTON 基于 Stable Diffusion v1.5 inpainit…...

接口测试(七)jmeter——参数化(RandomString函数)

一、RandomString函数 需求&#xff1a;模拟10个用户注册 1. 【工具】–>【函数助手对话框】 2. 选择RandomString函数 假设手机号码前3位设置为固定数值136&#xff0c;后8位可用RandomString函数随机产生数值 ① Random string length&#xff1a;8&#xff08;随机长度…...

simple_php

访问靶场 这里传入a和b参数&#xff0c;绕过三个if即可拿到flag a a a_GET[ a’ ];中是抑制报错信息的。 第一个if非常的抽象&#xff0c; if($a0 and $a){echo $flag1; }处理a 要输出flag1,a0&#xff0c;但是&#xff0c;在php中0被视为假也就是Flase 如果a0&#xff0…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…...

C++中vector类型的介绍和使用

文章目录 一、vector 类型的简介1.1 基本介绍1.2 常见用法示例1.3 常见成员函数简表 二、vector 数据的插入2.1 push_back() —— 在尾部插入一个元素2.2 emplace_back() —— 在尾部“就地”构造对象2.3 insert() —— 在任意位置插入一个或多个元素2.4 emplace() —— 在任意…...