揭秘线程安全:HashMap 的四大实用策略

 这篇文章,我们聊聊线程安全使用 HashMap 的四种技巧。

1 方法内部:每个线程使用单独的 HashMap

如下图,tomcat 接收到到请求后,依次调用控制器 Controller、服务层 Service 、数据库访问层的相关方法。

每次访问服务层方法 serviceMethod 时,都会在方法体内部创建一个单独的 HashMap , 将相关请求参数拷贝到 HashMap 里,然后调用 DAO 方法进行数据库操作。

每个 HTTP 处理线程在服务层方法体内部都有自己的 HashMap 实例,在多线程环境下,不需要对 HashMap 进行任何同步操作。

这也是我们使用最普遍也最安全的的方式,是 CRUD 最基本的操作。

2 配置数据:初始化写,后续只提供读

系统启动之后,我们可以将配置数据加载到本地缓存 HashMap 里 ,这些配置信息初始化之后,就不需要写入了,后续只提供读操作。

上图中显示一个非常简单的配置类 SimpleConfig ,内部有一个 HashMap 对象 configMap 。构造函数调用初始化方法,初始化方法内部的逻辑是:将配置数据存储到 HashMap 中。

SimpleConfig 类对外暴露了 getConfig 方法 ,当 main 线程初始化 SimpleConfig 对象之后,当其他线程调用 getConfig 方法时,因为只有读,没有写操作,所以是线程安全的。

3 读写锁:写时阻塞,并行读,读多写少场景

读写锁是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,而写锁则是互斥锁。

它的规则是:<strong style="font-size: inherit;line-height: inherit;color: rgb (255, 104, 39);"> 读读不互斥,读写互斥,写写互斥 </strong>,适用于读多写少的业务场景。

我们一般都使用 ReentrantReadWriteLock ,该类实现了 ReadWriteLock 。ReadWriteLock 接口也很简单,其内部主要提供了两个方法,分别返回读锁和写锁 。

 public interface ReadWriteLock {//获取读锁Lock readLock();//获取写锁Lock writeLock();
}

读写锁的使用方式如下所示:

  1. 创建 ReentrantReadWriteLock 对象,当使用 ReadWriteLock 的时候,并不是直接使用,而是获得其内部的读锁和写锁,然后分别调用 lock /unlock 方法;
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
  1. 读取共享数据 ;
Lock readLock = readWriteLock.readLock();
readLock.lock();
try {// TODO 查询共享数据
} finally {readLock.unlock();
}
  1. 写入共享数据;
Lock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {// TODO 修改共享数据
} finally {writeLock.unlock();
}

下面的代码展示如何使用 ReadWriteLock 线程安全的使用 HashMap :

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockCache {// 创建一个 HashMap 来存储缓存的数据private Map<String, String> map = new HashMap<>();// 创建读写锁对象private ReadWriteLock rw = new ReentrantReadWriteLock();// 放对象方法:向缓存中添加一个键值对public void put(String key, String value) {// 获取写锁,以确保当前操作是独占的rw.writeLock().lock();try {// 执行写操作,将键值对放入 mapmap.put(key, value);} finally {// 释放写锁rw.writeLock().unlock();}}// 取对象方法:从缓存中获取一个值public String get(String key) {// 获取读锁,允许并发读操作rw.readLock().lock();try {// 执行读操作,从 map 中获取值return map.get(key);} finally {// 释放读锁rw.readLock().unlock();}}
}

使用读写锁操作 HashMap 是一个非常经典的技巧,消息中间件 RockeMQ NameServer (名字服务)保存和查询路由信息都是通过这种技巧实现的。

另外,读写锁可以操作多个 HashMap ,相比 ConcurrentHashMap 而言,ReadWriteLock 可以控制缓存对象的颗粒度,具备更大的灵活性。

4 Collections.synchronizedMap : 读写均加锁

如下代码,当我们多线程使用 userMap 时,

static Map<Long, User> userMap = Collections.synchronizedMap(new HashMap<Long, User>());

进入 synchronizedMap 方法:

public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {return new SynchronizedMap<>(m);
}

SynchronizedMap 内部包含一个对象锁 Object mutex ,它本质上是一个包装类,将 HashMap 的读写操作重新实现了一次,我们看到每次读写时,都会用 synchronized 关键字来保证操作的线程安全。

虽然 Collections.synchronizedMap 这种技巧使用起来非常简单,但是我们需要理解它的每次读写都会加锁,性能并不会特别好。

5 总结

这篇文章,笔者总结了四种线程安全的使用 HashMap 的技巧。

1、方法内部:每个线程使用单独的 HashMap

这是我们使用最普遍,也是非常可靠的方式。每个线程在方法体内部创建 HashMap 实例,在多线程环境下,不需要对 HashMap 进行任何同步操作。

2、 配置数据:初始化写,后续只提供读

中间件在启动时,会读取配置文件,将配置数据写入到 HashMap 中,主线程写完之后,以后不会再有写入操作,其他的线程可以读取,不会产生线程安全问题。

3、读写锁:写时阻塞,并行读,读多写少场景

读写锁是一把锁分为两部分:读锁和写锁,其中读锁允许多个线程同时获得,而写锁则是互斥锁。

它的规则是:<strong style="font-size: inherit;line-height: inherit;color: rgb (255, 104, 39);"> 读读不互斥,读写互斥,写写互斥 </strong>,适用于读多写少的业务场景。

使用读写锁操作 HashMap 是一个非常经典的技巧,消息中间件 RockeMQ NameServer (名字服务)保存和查询路由信息都是通过这种技巧实现的。

4、Collections.synchronizedMap : 读写均加锁

Collections.synchronizedMap 方法使用了装饰器模式为线程不安全的 HashMap 提供了一个线程安全的装饰器类 SynchronizedMap。

通过 SynchronizedMap 来间接的保证对 HashMap 的操作是线程安全,而 SynchronizedMap 底层也是通过 synchronized 关键字来保证操作的线程安全。

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

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

相关文章

【数据结构-差分】【hard】力扣995. K 连续位的最小翻转次数

给定一个二进制数组 nums 和一个整数 k 。 k位翻转 就是从 nums 中选择一个长度为 k 的 子数组 &#xff0c;同时把子数组中的每一个 0 都改成 1 &#xff0c;把子数组中的每一个 1 都改成 0 。 返回数组中不存在 0 所需的最小 k位翻转 次数。如果不可能&#xff0c;则返回 -…

C++ 第三讲:内存管理

C 第三讲&#xff1a;内存管理 1.C内存分布2.内存管理方式2.1C语言内存管理方式2.2C内存管理方式2.2.1new\delete操作内置类型2.2.2new\delete操作自定义类型 3.operator new与operator delete函数4.new和delete实现原理4.1内置类型4.2自定义类型 5.定位new5.1内存池的基本了解…

防静电段码省电LCD驱动高抗干扰液晶驱动芯片VKL128稳定性好,超低工作电流,低休眠电流-技术开发资料

VKL128是一个点阵式存储映射的LCD驱动器&#xff0c;可支持最大128点&#xff08;32SEGx4COM&#xff09;的LCD屏。单片机可通过I2C接口配置显示参数和读写显示数据&#xff0c;可配置4种功耗模式&#xff0c;也可通过关显示和关振荡器进入省电模式。其高抗干扰&#xff0c;低功…

华为AR100-S路由器恢复出厂设置

华为AR100-S路由器浏览器访问web界面&#xff0c;出错的解决办法&#xff0c;之前讨论过&#xff0c;详情点这里&#xff1a; https://blog.csdn.net/weixin_62598385/article/details/142215136 这个路由器&#xff0c; 之前是装修公司安装的&#xff0c; 多年前的事了&#…

linux 操作系统下df命令介绍和案例

linux 操作系统下df命令介绍和案例 df 命令是 Linux 系统中用于显示文件系统磁盘空间使用情况的工具。它提供了关于已用空间、可用空间和文件系统总大小的信息&#xff0c;帮助用户管理和监控磁盘使用情况 df 命令概述 基本语法 bash df [选项] [文件系统] 选项&#xf…

【科技论文写作与发表】论文分类

目录 一、实验性论文特点写作结构适用学科方向 二、报道性论文特点写作结构适用学科方向 三、理论性论文特点写作结构适用学科方向 一、实验性论文 通过科学实验获得的数据和结果进行详细阐述和分析&#xff0c;检验某一科学理论或假说。 特点 详细描述实验目的、实验设计、实…

【Python基础】Python文件处理

本文收录于 《Python编程入门》专栏&#xff0c;从零基础开始&#xff0c;分享一些Python编程基础知识&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、打开文件三、读取文件内容四、写入文件内容五、高级文件操作六、总结 一、前言 ​ 在Python中&am…

HX1838红外接收模块-红外遥控(输入捕获)

目录 前言 程序设计思路 驱动代码 Remote.h Remote.c 前言 输入捕获测量电平持续时间&#xff1a; 【STM32】TIM输入捕获测量电平持续时间_单片机程序怎么测量低电平时长-CSDN博客 红外接收管介绍外部中断和状态机实现按键检测&#xff1a; HX1838红外接收模块-红外…

【Finetune】(一)、transformers之BitFit微调

文章目录 0、参数微调简介1、常见的微调方法2、代码实战2.1、导包2.2、加载数据集2.3、数据集处理2.4、创建模型2.5、BitFit微调*2.6、配置模型参数2.7、创建训练器2.8、模型训练2.9、模型推理 0、参数微调简介 参数微调方法是仅对模型的一小部分的参数&#xff08;这一小部分可…

微信小程序案例:比较数字大小(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

使用Postman轻松搞定文件上传测试

postman经常用于接口测试&#xff0c;但是上传文件参数还是蛮复杂的&#xff0c;记录下过程 01 上传文件参数 1.选择请求方式 选择post请求方式&#xff0c;输入请求地址 2.填写Headers Key&#xff1a;Content-Type &#xff1b; Value&#xff1a;multipart/form-data …

【机器学习】OpenCV高级图像处理

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 OpenCV高级图像处理图像滤波线性滤波高斯滤波均值滤波双边滤波 非线性滤波中值滤…

一文带你快速了解GPT 最新模型 o1!国内直接使用 !

一、GPT-o1简介 北京时间9月13日午夜&#xff0c;OpenAI 正式公开一系列全新 AI 大模型&#xff0c;旨在专门解决难题。这是一个重大突破&#xff0c;新模型可以实现复杂推理&#xff0c;一个通用模型解决比此前的科学、代码和数学模型能做到的更难的问题&#xff0c;OpenAI 发…

【Unity杂谈】iOS 18中文字体显示问题的调查

一、问题现象 最近苹果iOS 18系统正式版推送&#xff0c;周围升级系统的同事越来越多&#xff0c;有些同事发现&#xff0c;iOS 18上很多游戏&#xff08;尤其是海外游戏&#xff09;的中文版&#xff0c;显示的字很奇怪&#xff0c;就像一些字被“吞掉了”&#xff0c;无法显示…

微信如何发布学生查分?教师平台推荐!

学校和老师们都在面临着一个共同的问题&#xff1a;如何高效、便捷地发布学生成绩查询信息&#xff1f;在这个数字化时代&#xff0c;传统的纸质通知和口头传达方式已经无法满足家长和学生的需求。幸运的是&#xff0c;有了易查分这样的在线工具&#xff0c;发布学生查分变得简…

李章虎律师捐资设立“前沿技术产业化专项基金”

随着新一轮技术革命和产业变革的加速演进&#xff0c;全球科技创新进入空前活跃的时期&#xff0c;以人工智能、大数据、区块链等为代表的前沿技术革命方兴未艾&#xff0c;全新的产业格局正在逐渐形成。为聚焦世界前沿技术商业转化应用发展&#xff0c;助力更多企业转型升级赋…

芯片封装是什么?芯片封装中芯片环氧胶的应用有哪些?

芯片封装是什么&#xff1f;芯片封装中芯片环氧胶的应用有哪些&#xff1f; 芯片封装是什么&#xff1f; 芯片封装是集成电路&#xff08;IC&#xff09;制造过程中的关键步骤&#xff0c;它包括以下几个要点&#xff1a; 功能与目的&#xff1a;封装为芯片提供物理保护&#…

【HarmonyOS 】编译报错:Install Failed: error: failed to install bundle

此问题是由于支付宝sdk兼容性造成的&#xff0c;目前只能删除支付宝sdk依赖&#xff0c;如下图所示操作&#xff0c;删除后需要点右上角的 Sync Now&#xff0c;并等待 Sync 结束 删除后还需要点右上角的 Sync Now&#xff0c;并等待 Sync 结束 uniapp解决方案&#xff1a; htt…

重磅首发!2024中国大模型行业应用前景及现状分析——建议收藏观看

本报告由深圳前瞻产业研究院、首钢基金CANPLUS联合华为云共同出品。 报告显示&#xff0c;2023年我国AI大模型行业规模已达到147亿元。AI大模型的行业应用及技术进步能有效提升各行业生产要素的产出效率&#xff0c;并提高了数据要素在生产要素组合中的地位。供给方面&#xf…

LY3315 SOT23-6 集成充电与电机驱动的控制芯片

想要更方便、高效地管理电池充电和驱动电机&#xff1f;那么LY3315是你理想的选择&#xff01;LY3315是一款集成了锂电池充电管理模块、电机驱动模块、马达续流二极管、按键档位控制、保护模块的全集成电机驱动控制芯片。它不仅具备强大的功能&#xff0c;还具有超低的待机电流…