【并发设计模式】聊聊线程本地存储模式如何实现的线程安全

前面两篇文章,通过两阶段终止的模式进行优雅关闭线程,利用数据不变性的方式保证数据安全,以及基于COW的模式,保证读数据的安全。本篇我们来简述下如果利用线程本地存储的方式保证线程安全。

首先一个大前提就是并发问题,其实就是多个线程之间读写共享数据,那么COW是通过将数据读和写分离。而从不共享数据的角度看,那么每个线程都存储一份数据。那么就不会存在线程安全。也就是说线程T1维护一个变量i 自己操作,而线程T2也维护一个变量i。 T1对i 操作,不会影响到T2的i值。

Java中就是通过ThreadLocal的方式实现。

实现原理

具体的工作原理就是线程Thread持有ThreadLocalMap变量,而ThreadLocalMap其实是ThreadLocal中的一个静态内部类。而ThreadLocalMap其实就是数组结构,key对应的线程this。value对应的是线程设置的值。
在这里插入图片描述


public class Thread implements Runnable {/*** ThreadLocal 的 ThreadLocalMap 是线程的一个属性,所以在多线程环境下 threadLocals 是线程安全的*/ThreadLocal.ThreadLocalMap threadLocals = null;}

public class ThreadLocal<T> {public T get() {// 返回当前 ThreadLocal 所在的线程Thread t = Thread.currentThread();// 从线程中拿到 ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null) {// 从 map 中拿到 entryThreadLocalMap.Entry e = map.getEntry(this);// 如果不为空,读取当前 ThreadLocal 中保存的值if (e != null) {@SuppressWarnings("unchecked")T result = (T) e.value;return result;}}// 若 map 为空,则对当前线程的 ThreadLocal 进行初始化,最后返回当前的 ThreadLocal 对象关联的初值,即 valuereturn setInitialValue();}/*** 初始化 ThreadLocalMap,并存储键值对 <key, value>,最后返回 value** @return value*/private T setInitialValue() {// 获取为 ThreadLocal 对象设置关联的初值T value = initialValue();Thread t = Thread.currentThread();// 返回当前线程 t 持有的 mapThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {// 为当前线程初始化 map,并存储键值对 <t, value>createMap(t, value);}return value;}/*** 为当前 ThreadLocal 对象关联 value 值** @param value 要存储在此线程的线程副本的值*/public void set(T value) {// 返回当前 ThreadLocal 所在的线程Thread t = Thread.currentThread();// 返回当前线程持有的mapThreadLocalMap map = getMap(t);if (map != null) {// 如果 ThreadLocalMap 不为空,则直接存储<ThreadLocal, T>键值对map.set(this, value);} else {// 否则,需要为当前线程初始化 ThreadLocalMap,并存储键值对 <this, firstValue>createMap(t, value);}}/*** 清理当前 ThreadLocal 对象关联的键值对*/public void remove() {// 返回当前线程持有的 mapThreadLocalMap m = getMap(Thread.currentThread());if (m != null) {// 从 map 中清理当前 ThreadLocal 对象关联的键值对m.remove(this);}}/*** 返回当前线程 thread 持有的 ThreadLocalMap** @param t 当前线程* @return ThreadLocalMap*/ThreadLocalMap getMap(Thread t) {return t.threadLocals;}

在这里插入图片描述
所以通过以上的方式可以保证每个线程内部保存一个Map数组,但是对应的key确实一个软引用,具体的介绍,另一篇文章有详细介绍就不说了总体上就是

【Java并发】从simpleDateFormart聊聊threadlocal原理机制

  • 对象的强软弱虚引用
  • threadlocal的原理
  • 对象内存泄漏

实际应用

因为threadlocal是和线程绑定的,所以可以很自然的就采用在线程级别做一些事情。

1.切换数据库
比如我们在切换数据的时候,就可以通过threadlocal进行操作。
比如当前默认就是从库,但是想要从主库切到从库上,就可以进行通过threadlocal进行使用。

        DynamicDataSourceHolder.setDataSourceTypeMaster();boolean updateResult;try {xxxxx // 业务代码} finally {DynamicDataSourceHolder.clearDataSourceType();}
public final class DynamicDataSourceHolder {private static ThreadLocal<String> threadLocal = new ThreadLocal();public static int dataSourceMasterSize;public static int dataSourceSlaveSize;private static Random random = new Random();private DynamicDataSourceHolder() {}public static String getDataSourceType() {if (null == threadLocal.get()) {setDataSourceTypeSlave();}return (String)threadLocal.get();}public static void setDataSourceTypeMaster() {threadLocal.set("MASTER");}public static void setDataSourceTypeSlave() {int randomSlave = random.nextInt(dataSourceSlaveSize);threadLocal.set("SLAVE" + (randomSlave + 1));}public static void clearDataSourceType() {threadLocal.remove();}
}

通过这种方式,可以很方便的进行切换数据库。

2.第二种场景
那就是比如我们需要针对线程级别进行添加整个链路的相关信息,或者存储相关数据。或者通过AOP注入的方式,前后执行一些方法。

好了今天比较简单,就到这里。

推荐阅读

保姆级教学,22张图揭开ThreadLocal

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

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

相关文章

【六袆 - Framework】vue3入门;vue框架的特点矩阵列举;Vue.js 工作原理

vue框架的特点 Vue.js的特点展开叙述Vue.js的工作原理展开叙述 官方文档&#xff1a; https://cn.vuejs.org/guide/introduction.html Vue.js的特点 ┌────────────────────┬────────────────────────────────────…

yolov5 主要流程

1.介绍 本文包含了有关yolov5目标检测的基本流程&#xff0c;包括模型训练与模型部署&#xff0c;旨在帮助小伙伴们建立系统的认知&#x1f496;&#x1f496; YOLO是 "You only look once "的首字母缩写&#xff0c;是一个开源软件工具&#xff0c;它具有实时检测…

Spring Boot 后端服务开发学习计划

一、专业技能提升 Spring Boot 深入学习与实践&#xff1a; 学习并理解Spring Boot的核心原理和特性&#xff0c;如自动配置、starter、Actuator等。实践开发复杂的后端API&#xff0c;包括RESTful API设计、异常处理、安全控制等。熟练使用Spring Boot与Mariadb的集成&#xf…

昇腾910平台安装驱动、固件、CANN toolkit、pytorch

本文使用的昇腾910平台操作系统是openEuler&#xff0c;之前没了解过&#xff0c;不过暂时感觉用起来和centOS差不多。系统架构是ARM&#xff0c;安装包基本都是带aarch64字样&#xff0c;注意和x86_64区别开&#xff0c;别下错了。 安装依赖 cmake 通过yum安装的cmake版本较…

【国产化适配】银河麒麟v10 sp2 aarch64 制作openssh 9.6p1 rpm——筑梦之路

银河麒麟v10制作openssh 9.1/9.2/9.3/9.4/9.5 p1 rpm —— 筑梦之路_annot install both openssh-9.2p1-1.ky10.aarch64 an-CSDN博客 准备源码 https://src.fedoraproject.org/repo/pkgs/openssh/x11-ssh-askpass-1.2.4.1.tar.gz/8f2e41f3f7eaa8543a2440454637f3c3/x11-ssh-a…

一体化、一站式!智能视频客服加码全媒体云呼叫中心能力

凭借对电话、短信、邮件、社交媒体、视频等数种沟通渠道强大的统一集成能力&#xff0c;全媒体云呼叫中心已跃升成为现代企业客户服务的核心工具&#xff0c;高效便捷地为企业提供客户服务。而随着消费者需求愈加多元化和个性化&#xff0c;传统的语音通话方式已无法满足部分消…

PHP序列化总结1--序列化和反序列化的基础知识

序列化和反序列化的作用 1.序列化&#xff1a;将对象转化成数组或者字符串的形式 2.反序列化&#xff1a;将数组或字符串的形式转化为对象 为什么要进行序列化 这种数据形式中间会有很多空格&#xff0c;不同人有不同的书写情况&#xff0c;可能还会出现换行的情况 为此为了…

【qt】解决qt里编辑qss后失效问题(qt编码问题)

1、先创建qss文本stylesheet.qss 以按钮为例 QPushButton {background-color:rgb(240,255,255);color: rgb(0, 0, 2);border-style: outset;border-color: beige;border-radius: 10px; }/* hover按钮悬浮&#xff0c;鼠标悬浮在按钮上的状态&#xff0c;按钮颜色 */QPushButto…

Qt/C++音视频开发62-电子放大/按下选择区域放大显示/任意选取区域放大

一、前言 电子放大这个功能思考了很久&#xff0c;也是一直拖到近期才静下心来完整这个小功能&#xff0c;这个功能的前提&#xff0c;主要得益于之前把滤镜打通了&#xff0c;玩出花样来了&#xff0c;只要传入对应的滤镜字符串&#xff0c;就可以实现各种各样的效果&#xf…

工业4G 物联网网关——机房动环监控系统应用方案介绍

机房动环监控系统是什么&#xff1f;机房动环监控系统的全称为机房动力环境监控系统&#xff0c;是一套安装在机房内的监控系统&#xff0c;可以对分散在机房各处的独立动力设备、环境和安防进行实时监测&#xff0c;统计和分析处理相关数据&#xff0c;第一时间侦测到故障发生…

微机原理12练习题答案

一、单项选择题(本大题共15小题,每小题3分,共45分。在每小题给出的四个备选项中,选出一个正确的答案。〕 十进制正数56的 8位二进制补码是(D)。A. 00011001 B. 10100110 C. 10011001 D. 00100110 若栈顶的物理地址为20100H,当执行完指令PUSH AX 后,栈顶的物理地址是(D)A. 2…

Java InputStream OutputStream

OutputStream 轉 InputStream Case 1 toByteArray ByteArrayOutputStream out new ByteArrayOutputStream(); new ByteArrayInputStream(out.toByteArray()) Case2 從Output字節讀取Input ByteArrayOutputStream out new ByteArrayOutputStream();byte[] bs new byte[…

Livox-Mid-360 固态激光雷达ROS格式数据分析

前言&#xff1a; Livox-Mid-360 官方采用livox_ros_driver2ROS功能包发布ROS格式的数据&#xff0c;livox_ros_driver2可以把Livox原始雷达数据转化成ROS格式并以话题的形式发布出去。 下面列举一些雷达的基本概念&#xff1a; 点云帧&#xff1a;雷达驱动每次向外发送的一…

ubuntu 编译内核报错

Ubuntu 编译 Linux 内核经常会遇到如下错误&#xff1a; 如果报错 canonical-certs.pem&#xff1a; 如下&#xff1a; make[1]: *** No rule to make target ‘debian/canonical-certs.pem’, needed by ‘certs/x509_certificate_list’. Stop. make: *** [Makefile:1868: …

UE4运用C++和框架开发坦克大战教程笔记(十二)(第37~39集)

UE4运用C和框架开发坦克大战教程笔记&#xff08;十二&#xff09;&#xff08;第37~39集&#xff09; 37. 延时事件系统38. 协程逻辑优化更新39. 普通按键绑定 37. 延时事件系统 由于梁迪老师是写 Unity 游戏出身的&#xff0c;所以即便 UE4 有自带的 TimeManager 这样的延时…

AIGC开发:调用openai的API接口实现简单机器人

简介 开始进行最简单的使用&#xff1a;通过API调用openai的模型能力 OpenAI的能力如下图&#xff1a; 文本生成模型 OpenAI 的文本生成模型&#xff08;通常称为生成式预训练 Transformer 或大型语言模型&#xff09;经过训练可以理解自然语言、代码和图像。这些模型提供文…

RabbitMQ消息存储JSON格式反序列化

如果发送消息消息体为实体类对象数据&#xff0c;交换机接收消息经由路由键发送给队列。需要实现数据反序列化操作。实现JSON格式的反序列化操作 Rabbitmq的反序列化接口 MessageConverter&#xff0c;它的实现类有 Jackson2JsonMessageConverter的反序列化实现类&#xff0c…

数据仓库系列01-规划篇

企业在构建数据仓库时&#xff0c;首先需要整体上对数据仓库进行规划&#xff0c;制定规范。数仓架构师需要对数仓分层、业务分类、数据域、业务过程、数据集市、主题域进行设计。这样模型设计时&#xff0c;可以将模型关联到数仓分层、业务分类、数据域、业务过程等对所建模型…

openssl的 openssl.cnf配置文件详解

背景&#xff1a;在上一篇文中&#xff0c;提到要写一篇openssl 配置文件详解的&#xff0c;这就来了~~~ find / -name openssl.cnf /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl.cnf&#xff0c;该文件主要设置了证书请求、签名、crl相关的配置。主要相关的伪命令为ca和req…

HarmonyOS page生命周期函数讲解

下面 我们又要看一个比较重要的点了 页面生命周期 页面组件有三个生命周期 onPageShow 页面显示时触发 onPageHide 页面隐藏时触发 onBackPress 页面返回时触发 这里 我们准备两个组件 首先是 index.ets 参考代码如下 import router from ohos.router Entry Component struc…