JavaEE 第16节 线程安全的集合类

目录

前言

顺序表

队列

哈希表

1、Hashtable

2、ConcurrentHashMap(重点)


前言

本文章主要介绍在多线程环境下如何线程安全的使用一些常用的集合类(顺序表和哈希表)。


顺序表

1、自己使用同步锁机制(synchornized和ReentrantLock)保证线程安全

2、Collections.synchronizedList()

它是 Java 中 java.util.Collections 类提供的一个基于synchronized关键字,给List加锁的静态方法,用于创建安全的List。

3、写时拷贝

Java中提供了CopyOnWrit这个容器来解决线程安全问题。

它的解决思路是:

在多个线程读取容器中的数据的时候不会做任何操作,但是当一个线程要修改容器中的数据的时候,不能立刻修改当前引用所指向的数据,而是重新拷贝一份相同的容器,在这个新的容器中去修改数据。在写入数据的时候,其他线程只能读取旧容器中的数据。等修改完毕,把引用现在已经修改好的新的容器(引用的赋值是一个原子操作)。

优点:

 在读多写少的环境下,因为没有加锁,所以没有锁开销,也没有锁竞争,性能高。

缺点:

  • 不能实现多个线程同时修改数据,因此不适用于写多读少的场景。
  • 因为拷贝的参与,所以当数据量本身很大的情况下会消耗内存,并且时间开销也会增大。

队列

关于队列的线程安全,自然使用到的是阻塞队列:

1. ArrayBlockingQueue:   基于数组实现的阻塞队列

2. LinkedBlockingQueue: 基于链表实现的阻塞队列

3. PriorityBlockingQueue: 基于堆实现的带优先级的阻塞队列

4. TransferQueue:             最多只包含⼀个元素的阻塞队列

关于阻塞队列的详细介绍这里就不展开了,在同专栏第9节专门讲解了。


哈希表

HashMap本身是线程不安全的,在多线程环境下可以使用Hashtable/ConcurrentHashMap

1、Hashtable

Hashtable这个类只是对HashMap进行简单的加锁,也就是在关键方法上增加了synchronized关键字:

这就导致只要多个线程同时调用put或者get方法,即使他们想要访问的不是同一组数据,照样会进入阻塞状态,也就是所冲突,极大的影响了运行效率。

另外,如何在某一个线程使用put操作时,如果需要扩容,那么就会由当前线程进行扩容,如果涉及大量的元素拷贝,那么多线程的运行效率又会大幅降低。

总的来讲,HashTable相当于对整个哈希桶上了锁:

2、ConcurrentHashMap[推荐使用]

为了降低锁冲突概率,ConcurrentHashMap做出了以下优化:

  • 读操作:没有加锁,但是加了volatile保证内存可见。
  • 写操作:对每一个链表都进行了加锁(synchronized实现),如果写入数组中不同的下标,那么就不会发生所冲突,如图:
  • CAS的使用:在锁竞争并不激烈的情况下,采用CAS来更新size(键值对 增加/减少),这种方式不用加锁,更轻量,提高运行效率(jdk8引入)。
     
  • 分散计数器槽:在锁竞争激烈时,使用CAS就可能出现忙等的情况,使用CAS修改size就不太好了。为了避免多个线程同时修改同一个数据(size),ConcurrentHashMap引入了分散计数器槽(Counter Cells),它的原理如下:

  1、分散计数器槽的初始化:

.     ◦ 当多个线程进行插入或删除操作时,如果检测到对单个计数器的竞争变得激烈,ConcurrentHashMap会创建一个计数器槽数组。

      ◦ 这个数组中的每个槽(cell)都可以独立地更新,从而减少竞争。

  2、计数器槽的更新:

     ◦ 当一个线程需要更新size(例如插入或删除元素)时,它会尝试使用CAS操作更新某个计数器槽。

     ◦ 如果更新失败(因为另一个线程同时在更新这个槽),该线程会尝试更新另一个槽,直到成功为止。

  3、计数器槽的汇总:

     ◦ 当需要获取ConcurrentHashMap的大小时,size()方法会遍历所有计数器槽,将它们的值相加得到总的元素数量。


Counter Cells的核心思想就是使用数组来降低并发负载。

  • 扩容方式:与HashTable让发现需要扩容的线程去完成整个扩容任务不同,Concurrent把扩容任务分散给了多个线程去完成,即“化整为零”。
    大致步骤如下:​​​​

1、创建一个比原来还要大的数组。

2、每一个线程都尝试去获取一个没有完成旧数组到新数组迁移任务的桶(链表),然后对桶进行数据迁移、重新哈希(使用CAS保证线程安全,同时提高整体效率)。

3、所有桶都完成了数据迁移后,数组引用从旧数组指向新数组,扩容才真正完成。


注意,在扩容期间:

1)如果有put(插入操作),先对旧桶进行哈希,如果旧桶没有数据迁移,那么就插入到旧桶中,如果已经数据迁移,就插入到新桶中。

2)如果是get(查找操作),先找旧桶,旧桶没有找到,找新桶。

这种扩容方式大大提高了并发性能(逐步迁移,降低阻塞概率)以及系统稳定性(扩容时,单个线程锁占用时间不会激增)。


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

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

相关文章

大数据技术

4v特点 volume(体量大) velocity(处理速度快) variety(数据类型多) value(价值密度低) 核心设计理念 并行化 规模经济 虚拟化 分布式系统满足需求 系统架构 大数据处理流程 结构化…

CocosCreator 3.8 IOS 热更新失败问题解决方案

CocosCreator 3.8 IOS 热更新失败问题解决方案 问题描述 Creator 版本: 3.8.0目标平台: ios 模拟器/真机重现方式:安卓构建版本生成的热更新包,上传到OSS,使用ios进行更新。 19:18:36 [ERROR]: [ERROR] file /Applica…

动态读取nacos中修改的项目配置文件

本项目用的还是springboot项目,咱们直接上代码 一:首先看下nacos中需要动态获取的属性 二:把需要动态读取的配置类中的属性整理一个实体类 mport lombok.Data; import org.springframework.boot.context.properties.ConfigurationPropert…

PyCharm汉化:简单一步到胃!PyCharm怎么设置中文简体

最近在弄python的项目 一起加油哦 步骤: PyCharm的汉化可以通过两种主要方法完成: 方法一:通过PyCharm内置的插件市场安装中文语言包 1. 打开PyCharm,点击File -> Settings(在Mac上是PyCharm -> Preferences…

[报错] nvcc -V 找不到

报错: nvcc : 无法将“nvcc”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,ObjectNotFound: (nvcc:String) [], CommandNotFoundExceptionFullyQualifiedErrorId : CommandNotFoundException 找不到 nvcc -V,试过…

erlang学习:用OTP构建系统1

书上案例学习并测试 23.1 通用事件处理 -module(event_handler). %% API -export([make/1, add_handler/2, event/2]).%% 制作一个“什么都不干”的事件处理器Name(一个原子)。这样消息就有地方发送了。 make(Name) ->register(Name, spawn(fun() -…

SoftMaker Office Pro 2024:高效办公的全方位解决方案

SoftMaker Office Pro 2024是一款集高效、专业、全面于一体的办公软件套件,专为满足现代办公需求而设计。这款套件不仅包含了文字处理、电子表格、演示文稿等核心功能,还集成了项目管理、文档管理和客户管理等实用工具,为用户提供了全方位的办…

微前端集成优化:让所有子应用体积更小,加载更快!

简介 随着前端的日益发展,微前端架构越来越受到青睐。它通过将前端应用拆分为多个独立的子应用,每个子应用可以独立开发、部署和运行,从而提升了开发效率和团队协作。目前主流的微前端方案应该是qiankun了。 以笔者公司为例,采用…

Go锁 详解

锁 - Go 函数并发编程中,锁是一种同步机制,用于协调对共享资源的访问,防止数据竞争 - Go 中提供了多种类型的锁,每种锁都有不同的特性和适用场景类型 互斥锁(mutex) 基础锁,只能同时允许一个 g…

okhttp异步请求连接阻塞问题排查

表现: 使用okhttp请求外部大模型接口时,当并发在2-5左右,出现请求被阻塞在建立http连接之前,阻塞时间超长(>20s,从日志看有160s存在)。但是httpconfig的connTimeout时间配置为100s&#xff…

python如何调用函数库

python对函数库引用的第一种方式 格式是&#xff1a; import<库名> 例如&#xff1a; import turtle 如果需要用到函数库中函数&#xff0c;需要使用&#xff1a; <库名>.<函数名> 例如&#xff1a; import turtleturtle.fd(100) python对函数库引用的第…

【Qt】网格布局管理器QGridLayout

网格布局管理器QGridLayout Qt中提供QGridLayout用来实现网格布局的效果。 核心属性 整体和 QVBoxLayout 以及 QHBoxLayout 相似. 但是设置 spacing 的时候是按照垂直⽔平两个 ⽅向来设置的. 属性说明 layoutLeftMargin 左侧边距 layoutRightMargin 右侧边距 layoutTo…

这款SpringBoot+Vue酒店管理系统,你绝对值得拥有

这款SpringBootVue酒店管理系统&#xff0c;你绝对值得拥有 一、项目介绍二、相关技术栈三、运行步骤后端运行前端运行 四、项目演示总结 源码获取请关注最下方公众号并后台回复【酒店管理系统boot】即可获取&#xff01; 大家好&#xff0c;这里是程序猿代码之路&#xff01;随…

一、基于Vue3的开发-环境搭建【pnpm】安装

基于Vue3开发环境搭建 1、npm 的安装1.1、下载参考地址1.2、安装1.3、设置为国内镜像2、pnpm的安装2.1、启动PowerShell注意事项2.1、 安装2.2、常用命令3、创建项目1、npm 的安装 1.1、下载参考地址 //下载参考地址:https://nodejs.cn/download/#google_vignette下载界面 …

景联文科技提供语音采集服务:开启智能交互新纪元

随着人工智能技术的飞速发展&#xff0c;语音交互已成为连接人与智能设备的重要桥梁。无论是智能助手、智能家居还是自动驾驶汽车&#xff0c;语音识别技术都是其背后不可或缺的核心力量。 为了满足各行各业对高质量语音数据的需求&#xff0c;景联文科技凭借自身强大的数据采集…

【Python】简单的数据类型——int、float、bool、str

目录 1. 整数类型 int 2. 浮点数类型 float 3. 布尔类型 bool 4. 字符串 str 5. 格式化输出 6. 类型转换 6.1 隐式类型转换 6.2 显示类型转换 7. 标准输入 1. 整数类型 int a 10 print(type(a)) print(type(-2))<class int> <class int>测试整型能表示的…

0818-0824面试题目和复习整理

根据面试问的问题整理一下 1. 并查集 int n 1005; // n根据题目中节点数量而定&#xff0c;一般比节点数量大一点就好 vector<int> father vector<int> (n, 0); // C里的一种数组结构// 并查集初始化 void init() {for (int i 0; i < n; i) {father[i] i;…

Linux安装Docker与基本指令

1、什么是Docker Dokcer是一种开源平台&#xff0c;主要用于创建、部署和管理容器化应用程序&#xff0c;它通过将应用程序以及所有的依赖打包到一个轻量级的、可移植的容器中&#xff0c;使得应用可以在任何环境中一致的运行! 1.1、Docker的优点 一致性和可移植性 跨环境一致…

大众集团25届校招社招网申入职SHL测评题库:综合能力测评、性格问卷、英语测评考什么?

恭喜您通过大众汽车(中国)科技有限公司的简历初。请点击下面的测评链接&#xff0c;在5天内完成测评&#xff0c;过期失效(例:3.11收到链接&#xff0c;3.15为最后一天有效期)。每位人选只有一次测评机会。 ​大众汽车入职测试细节: 1.性格问卷:25 分钟 2.综合能力:46 分钟&a…

upload-labs(Pass-18 ~ Pass-21)

1、Pass-18(条件竞争) 1、题目需要进行代码审计&#xff1a; <?php include ../config.php; include ../head.php; include ../menu.php;$is_upload false; $msg null;if(isset($_POST[submit])){$ext_arr array(jpg,png,gif);//白名单$file_name $_FILES[upload_fil…