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

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

前面两篇文章,通过两阶段终止的模式进行优雅关闭线程,利用数据不变性的方式保证数据安全,以及基于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

相关文章:

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

前面两篇文章&#xff0c;通过两阶段终止的模式进行优雅关闭线程&#xff0c;利用数据不变性的方式保证数据安全&#xff0c;以及基于COW的模式&#xff0c;保证读数据的安全。本篇我们来简述下如果利用线程本地存储的方式保证线程安全。 首先一个大前提就是并发问题&#xff…...

边缘计算网关:重新定义物联网数据处理

随着物联网&#xff08;IoT&#xff09;设备的爆炸式增长&#xff0c;数据处理和分析的需求也在迅速增加。传统的数据处理方式&#xff0c;将所有数据传输到中心服务器进行处理&#xff0c;不仅增加了网络负担&#xff0c;还可能导致数据延迟和安全问题。因此&#xff0c;边缘计…...

Linux之下载安装

rpm包管理 rpm介绍 rpm用于互联网下载包的打包及安装工具&#xff0c;他包含在某些linux分发版本中。他生成具有.rpm扩展名的文件。RPM是RedHat Package Manager(RedHat软件包管理工具&#xff09;的缩写&#xff0c;类似windows的steup.exe。 rpm包的查询指令 查询已经安装…...

【HarmonyOS开发】案例-记账本开发

OpenHarmony最近一段时间&#xff0c;简直火的一塌糊度&#xff0c;学习OpenHarmony相关的技术栈也有一段时间了&#xff0c;做个记账本小应用&#xff0c;将所学知识点融合记录一下。 1、记账本涉及知识点 基础组件&#xff08;Button、Select、Text、Span、Divider、Image&am…...

webrtc中的接口代理框架

文章目录 接口代理框架Proxy体系类结构导出接口 webrtc的实际运用PeerConnectionFactoyPeerConnection使用 接口代理框架 webrtc体系庞大&#xff0c;模块化极好&#xff0c;大多数模块都可以独立使用。模块提供接口&#xff0c;外部代码通过接口来使用模块功能。 在webrtc中通…...

【AIGC-图片生成视频系列-4】DreamTuner:单张图像足以进行主题驱动生成

目录 一. 项目概述 问题&#xff1a; 解决&#xff1a; 二. 方法详解 a) 整体结构 b) 自主题注意力 三. 文本控制的动漫角色驱动图像生成的结果 四. 文本控制的自然图像驱动图像生成的结果 五. 姿势控制角色驱动图像生成的结果 2023年的最后一天&#xff0c;发个文记录…...

Jupyter Notebook的10个常用扩展介绍

Jupyter Notebook&#xff08;前身为IPython Notebook&#xff09;是一种开源的交互式计算和数据可视化的工具&#xff0c;广泛用于数据科学、机器学习、科学研究和教育等领域。它提供了一个基于Web的界面&#xff0c;允许用户创建和共享文档&#xff0c;这些文档包含实时代码、…...

uniapp项目如何引用安卓原生aar插件(避坑指南三)

官方文档说明&#xff1a;uni小程序SDK 【彩带- 避坑知识点】 如果引用原生aar插件&#xff0c;都配置好之后&#xff0c;云打包&#xff0c;报不包含此插件&#xff0c;除了检查以下步骤流程外&#xff0c;还要检查一下是否上打包的原生插件aar流程有问题。 1.第一步在uniapp项…...

YOLOv8改进 | 检测头篇 | ASFF改进YOLOv8检测头(全网首发)

一、本文介绍 本文给大家带来的改进机制是利用ASFF改进YOLOv8的检测头形成新的检测头Detect_ASFF&#xff0c;其主要创新是引入了一种自适应的空间特征融合方式&#xff0c;有效地过滤掉冲突信息&#xff0c;从而增强了尺度不变性。经过我的实验验证&#xff0c;修改后的检测头…...

思维训练-怎样设计一个MQ

架构师需要做各种设计&#xff0c;要不断地提高自己的设计能力。这有没有方法可以训练呢&#xff1f;有的&#xff0c;就是看到什么、想到什么&#xff0c;就假设对面坐着产品经理&#xff0c;一起讨论怎么把它设计出来。比如怎样设计一个MQ 我&#xff1a;首先我确认一下需求。…...

RK3399平台入门到精通系列讲解(导读篇)21天挑战Linux系统开发

🚀返回总目录 文章目录 一、关于作者1、博主的联系方式2、支持二、需要具备的知识和工具1、需掌握知识点2、需了解的知识点三、通过系列博客可以学到什么1、本系列博文特色2、21天学习目标3、21天学习内容4、学习时间5、学习产出...

企业微信会话存档sdk报错:A fatal error has been detected by the Java Runtime Environment

错误信息 # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc0x00007f218f93485d, pid10, tid58 # # JRE version: OpenJDK Runtime Environment 18.9 (11.0.14.11) (build 11.0.14.11) # Java VM: OpenJDK 64-Bit Server VM 18.9…...

nginx-docker 搭建websocket反向代理

下载镜像 docker pull nginx复制出配置文件 将/etc/nginx/nginx.conf和/etc/nginx/conf.d/default.conf复制到本机 nginx.conf文件内容 user nginx; worker_processes auto;error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid;events {worker_c…...

blender插件开发

Quickstart — Blender Python API Blender Python 编程&#xff1a;关键概念 - 知乎 系列目录链接&#xff08;更新中&#xff0c;如无链接说明未更新&#xff09; [Blender Python] 列出/插入/删除物体&#xff0c;Blender数据对象 - 知乎 (zhihu.com)[Blender Python] 设…...

【数据结构】二叉搜索(查找/排序)树

一、二叉搜索树基本概念 1、定义 二叉搜索树&#xff0c;又称为二叉排序树&#xff0c;二叉查找树&#xff0c;它满足如下四点性质&#xff1a; 1&#xff09;空树是二叉搜索树&#xff1b; 2&#xff09;若它的左子树不为空&#xff0c;则左子树上所有结点的值均小于它根结…...

Vue:Vue与VueComponent的关系图

1.一个重要的内置关系&#xff1a;VueComponent.prototype.proto Vue.prototype 2.为什么要有这个关系&#xff1a;让组件实例对象&#xff08;vc&#xff09;可以访问到 Vue原型上的属性、方法。 案例证明&#xff1a; <!DOCTYPE html> <html lang"en"&…...

Elasticsearch8集群部署

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 本文记录在3台服务器上离线搭建es8.7.1版本集群。 1. 修改系统配置 1.1 hosts配置 在三台es节点服务器加入hostname解析&…...

【小白专用】c# 如何获取项目的根目录

1、取得控制台应用程序的根目录方法 方法1、Environment.CurrentDirectory 取得或设置当前工作目录的完整限定路径 方法2、AppDomain.CurrentDomain.BaseDirectory 获取基目录&#xff0c;它由程序集冲突解决程序用来探测程序集 2、取得Web应用程序的根目录方法 方法1、HttpRun…...

【PXIE301-208】基于PXIE总线架构的Serial RapidIO总线通讯协议仿真卡

板卡概述 PXIE301-208是一款基于3U PXIE总线架构的Serial RapidIO总线通讯协议仿真卡。该板卡采用Xilinx的高性能Kintex系列FPGA作为主处理器&#xff0c;实现各个接口之间的数据互联、处理以及实时信号处理。板卡支持4路SFP光纤接口&#xff0c;支持一个PCIe x8主机接口&…...

软件测试/测试开发丨Windows系统chromedriver安装与环境变量配置

一、selenium 环境配置 1、chrome 浏览器的安装与配置 目前比较常用的浏览器是 Google Chrome 浏览器&#xff0c;所以本教程以 chrome 为主&#xff0c;后面简介一下其他浏览器的环境配置。 &#xff08;1&#xff09;chrome 下载: www.google.cn/chrome/ &#xff08;2&a…...

【vim 学习系列文章 3.1 -- vim 删除 ^M】

请阅读【嵌入式开发学习必备专栏 之 VIM 专栏】 文章目录 ^M 来源^M 删除 ^M 来源 在 Vim 中打开文件时&#xff0c;您可能会遇到行尾的 ^M 字符&#xff0c;这通常是因为文件使用了 Windows 风格的回车换行符&#xff08;CRLF&#xff09;&#xff0c;而不是 Unix/Linux 风格…...

深入理解 C# 中的字符串比较:String.CompareTo vs String.Equals

深入理解 C# 中的字符串比较&#xff1a;String.CompareTo vs String.Equals 在处理字符串时&#xff0c;了解如何正确比较它们对于编写清晰、有效和可靠的 C# 程序至关重要。本文将深入探讨 C# 中的两个常用字符串比较方法&#xff1a;String.CompareTo 和 String.Equals&…...

DevOps持续交付之容器化CICD流水线

DevOps持续交付 随着DevOps⼤规模化的落地和应⽤&#xff0c;持续集成以及持续交付已经是⼀种常态的。CI指的是持续集成&#xff0c;使⽤的开源⼯具是Jenkins&#xff0c;CD指的是持续交付和持续部署&#xff0c;⼀个完整的软件开发⽣命周期为: 主要流程可以具体为: 构建阶段…...

Linux/Unix/国产化操作系统常用命令(二)

目录 后CentOS时代国产化操作系统国产化操作系统有哪些常用Linux命令关于Linux的LOGO 后CentOS时代 在CentOS 8发布后&#xff0c;就有了一些变化和趋势&#xff0c;可以说是进入了"后CentOS时代"。这个时代主要表现在以下几个方面&#xff1a; CentOS Stream的引入…...

基于SpringBoot的智慧生活商城系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的智慧生活商城系统,java…...

Vue框架引入Axios

首先已经创建好了 Vue 框架&#xff0c;安装好了 node.js。 没有完成的可按照此博客搭建&#xff1a;搭建Vue项目 之后打开终端&#xff0c;使用命令。 1、命令安装 axios 和 vue-axios npm install axios --save npm install vue-axios --save2、package.json 查看版本 在 p…...

EasyExcel 通过模板 导入、导出、下载模板

EasyExcel 通过模板 导入、导出、下载模板 import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor;import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; import java.io.…...

SAP ABAP通过代码解锁SM12中被锁定目标<转载>(RFC: ENQUEUE_READ和 ENQUE_DELETE)

原文链接&#xff1a;https://blog.csdn.net/sinat_38119716/article/details/121406275 备注 RFC:ENQUEUE_READ 读取的是SM12的数据 RFC:ENQUEUE_READ2 读取的是SMENQ的数据 SM12 和 SMENQ 的数据其实是一样的&#xff0c;只是一个是旧的TCODE 一个是新的 解锁用的都是RFC: …...

跳跃表原理及实现

一、跳表数据结构 跳表是有序表的一种&#xff0c;其底层是通过链表实现的。链表的特点是插入删除效率高&#xff0c;但是查找节点效率很低&#xff0c;最坏的时间复杂度是O(N)&#xff0c;那么跳表就是解决这一痛点而生的。 为了提高查询效率&#xff0c;我们可以给链表加上索…...

详解Vue3中的鼠标事件mousemove、mouseover和mouseout

本文主要介绍Vue3中的常见鼠标事件mousemove、mouseover和mouseout。 目录 一、mousemove——鼠标移动事件二、mouseover——鼠标移入事件三、mouseout——鼠标移出事件 下面是Vue 3中常用的鼠标事件mousemove、mouseover和mouseout的详解。 一、mousemove——鼠标移动事件 鼠…...

中山顺的网站建设/网站快照优化公司

8皇后问题&#xff08;改编&#xff09; 问题描述规则同8皇后问题&#xff0c;但是棋盘上每格都有一个数字&#xff0c;要求八皇后所在格子数字之和最大。输入格式一个8*8的棋盘。输出格式所能得到的最大数字和样例输入1 2 3 4 5 6 7 89 10 11 12 13 14 15 1617 18 19 20 21 22…...

定制做网站报价/阿里云免费域名

Word快捷键大全1、关于Word中的常见任务的快捷键表 1 Word中的常见任务快捷键一览表执行操作快捷键创建不间断空格CtrlShift空格键创建不间断连字符CtrlShift连字符 (-)使字符变为粗体CtrlB使字符变为斜体CtrlI为字符添加下划线CtrlU将字号减小一个值CtrlShift<将字号增大一…...

滁州市政府网站集约化建设/专业搜索引擎seo技术公司

http://blog.csdn.net/r91987/article/details/5435328 PPM文件格式分三种&#xff1a; 1. PPM灰度文件 文件头由3行文本组成&#xff0c;可由fgets读出 1&#xff09;第一行为“P2"&#xff0c;表示文件类型 2&#xff09;第二行为图像的宽度和高度 3&am…...

厦门电子商务网站建设/陕西网络推广公司

BDD - SpecFlow Context Injection 上下文依赖注入引言Context Injection如何使用 Context InjectionContext Injection 规则举例 1 共享字段举例 2 共享引用类自定义 Injection Framework 注入框架自定义 Container引言 BDD SpecFlow Scenario 在整个执行周期&#xff0c;Ste…...

赵县住房和城乡建设局网站首页/百度商城官网

如下图这个sql语句&#xff0c;扫描的行数成百万级以上的时候就可以使用分段查询12、避免在 where 子句中对字段进行 null 值判断对于null的判断会导致引擎放弃使用索引而进行全表扫描。13、不建议使用%前缀模糊查询例如LIKE “%name”或者LIKE “%name%”&#xff0c;这种查询…...

华为云自助建站/推广链接点击器app

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5;&#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密…...