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

目录

·前言

一、多线程环境使用 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。

·结尾

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机毕业设计 | springboot+vue凌云在线阅读平台 线上读书系统(附源码)

1,绪论 随着社会和网络技术的发展,网络小说成为人们茶钱饭后的休闲方式,但是现在很多网络小说的网站都是收费的,高额的收费制度是很多人接受不了的,另外就是很多小说网站都会有大量的弹窗和广告,这极大的影…

医学数据分析中的偏特征图可视化

在医学领域,我们经常需要处理复杂的数据模型,探索特征与目标变量之间的关系。偏特征图(Partial Dependence Plot, PDP)是一种强大的可视化技术,可以帮助我们更好地理解模型的行为。通过这种图形,我们可以直观地观察每个特征对模型…

零一万物新模型Yi-Lightning:超越GPT-4o

10月16日,零一万物发布了最新的旗舰模型Yi-Lightning(闪电),在中国大模型中首度超越 GPT-4o。它在国际权威盲测榜单 LMSYS 上取得了显著成绩,超越了硅谷知名 OpenAI 的 GPT-4o-2024-05-13 和 Anthropic Claude 3.5 Son…

关于iPhone 16 Pro评测视频评论区特征的多维度分析

1.项目背景 随着智能手机的迅速发展,消费者在选择新设备时越来越依赖于网络评价和用户反馈,B站作为中国领先的视频分享平台,聚集了大量科技评测内容,其中UP主的评论区成为用户讨论和交流的重要场所,特别是在iPhone 16…

基于SSM的汽车客运站管理系统【附源码】

基于SSM的汽车客运站管理系统(源码L文说明文档) 目录 4 系统设计 4.1 设计原则 4.2 功能结构设计 4.3 数据库设计 4.3.1 数据库概念设计 4.3.2 数据库物理设计 5 系统实现 5.1 管理员功能实现 5.1.1 管理员信息 5.1.2 车…

【程序员的逆袭】:在失业的阴影下寻找光明

故事摘要 在失业的阴霾中,一位程序员如何通过外包项目重燃希望之火?这个故事讲述了他的谋生手段,如何在压力之下,通过信息差赚取生活所需。 要点 信息的力量:赚钱的关键在于信息差,而非单纯的体力或脑力…

【轻量级聊天应用】Vocechat本地服务器部署结合cpolar异地即时通讯

文章目录 前言1. 拉取Vocechat2. 运行Vocechat3. 本地局域网访问4. 群晖安装Cpolar5. 配置公网地址6. 公网访问小结 7. 固定公网地址 前言 本文主要介绍如何在本地群晖NAS搭建一个自己的聊天服务Vocechat,并结合内网穿透工具实现使用任意浏览器远程访问进行智能聊天…

iTerm2 保持SSH远程连接

1、保持SSH远程连接的稳定,防止因闲置时间过长而断开连接 When idle, send ASCII code 35 every 60 seconds每60秒 输入# 2、客户端设置保持活动 设置客户端每隔60秒发送一次保活信号,总共尝试3次。 vim ~/.ssh/configHost *ServerAliveInterval 60…

python csv库

python csv库 水一水又是一篇,乐 读取 import csv # 打开 CSV 文件 with open(example.csv, moder, newline) as file: csv_reader csv.reader(file) # 读取文件头(可选) headers next(csv_reader) print(f"Headers: {heade…

w001基于SpringBoot的在线拍卖系统

🙊作者简介:多年一线开发工作经验,原创团队,分享技术代码帮助学生学习,独立完成自己的网站项目。 代码可以查看文章末尾⬇️联系方式获取,记得注明来意哦~🌹赠送计算机毕业设计600个选题excel文…

gateway 整合 spring security oauth2

微服务分布式认证授权方案 在分布式授权系统中,授权服务要独立成一个模块做统一授权,无论客户端是浏览器,app或者第三方,都会在授权服务中获取权限,并通过网关访问资源 OAuth2的四种授权模式 授权码模式 授权服务器将授…

【密码学】全同态加密张量运算库解读 —— TenSEAL

项目地址:https://github.com/OpenMined/TenSEAL 论文地址:https://arxiv.org/pdf/2104.03152v2 TenSEAL 是一个在微软 SEAL 基础上构建的用于对张量进行同态加密操作的开源Python库,用于在保持数据加密的状态下进行机器学习和数据分析。 Ten…

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

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

网络搜索引擎Shodan(4)

声明:学习视频来自b站up主 泷羽sec,如涉及侵权马上删除文章 声明:本文主要用作技术分享,所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险,并遵循相关法律法规。 感谢泷…

【JavaEE】【多线程】volatile,wait/notify

目录 一、volatile关键字1.1 内存可见性1.2 volatile解决内存可见性问题 二、wait和notify2.1 wait2.2 notify2.3 使用例子2.3.1 例子12.3.2 例子二 一、volatile关键字 volatile可以保证内存可见性,只能修饰变量。 1.1 内存可见性 在前面介绍线程不安全原因时介…

大数据开发扩展shell 笔记

大数据开发扩展shell 此笔记来自尚硅谷 学习目标 1 熟悉shell脚本的原理和使用 2 熟悉shell的编程语法 第一节 Shell概述 1)Linux提供的Shell解析器有: [atguiguhadoop101 ~]$ cat /etc/shells /bin/sh/bin/bash/sbin/nologin/bin/dash/bin/tcsh/b…

JCSA-Journal of Consumer Affairs

文章目录 一、征稿简介二、重要信息三、服务简述四、投稿须知五、联系咨询 一、征稿简介 二、重要信息 期刊官网:https://ais.cn/u/3eEJNv 三、服务简述 Journal of Consumer Affairs由美国消费者利益委员会(ACCI)拥有,成立于1…

淘宝商品详情的“侦探游戏”:如何用API接口揭开数据的面纱

在这个充满神秘数据的电商世界里,淘宝商品详情就像是一个个隐藏的宝藏,等待着我们去发掘。而API接口,就是我们的“侦探工具”,帮助我们快速揭开这些宝藏的面纱。今天,我们就来一场幽默的“侦探游戏”,看看如…

炒股VS炒CSGO游戏装备,哪个更好做

这个项目,赚个10%都是要被嫌弃的 虽然天天都在抒发自己对股市的看法,但自己自始至终也没有买进任何一支股票。之所以对这个话题感兴趣,着实是因为手上的游戏搬砖项目也是国际性买卖,跟国际形势,国际汇率挂钩&#xff…

C++线程池手写实现

1.Thread类的封装 封装Thread类&#xff0c;使其可以直接在外部调用对象的start,detach,join和cancel等方法来实现对线程的操作 1.1代码 //Thread.h// // Created by crab on 2024/10/20. //#ifndef THREAD_H #define THREAD_H#include <pthread.h>class Thread { pub…