线程本地变量-ThreadLocal

一、ThreadLocal简介

ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:

因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这是也是 ThreadLocal 命名的由来。
既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题

  • ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。
  • ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

总的来说,ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

下图可以增强理解:

ThreadLocal在使用过程中状态 

二、ThreadLocal与Synchronized的区别

ThreadLocal<T>其实是与线程绑定的一个变量。ThreadLocal和Synchonized都用于解决多线程并发访问。

但是ThreadLocal与synchronized有本质的区别:

1、Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离

2、Synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。而Synchronized却正好相反,它用于在多个线程间通信时能够获得数据共享。

一句话理解ThreadLocal,threadlocl是作为当前线程中属性ThreadLocalMap集合中的某一个Entry的key值Entry(threadlocl,value),虽然不同的线程之间threadlocal这个key值是一样,但是不同的线程所拥有的ThreadLocalMap是独一无二的,也就是不同的线程间同一个ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个value变量地址是一样的。

三、ThreadLocal的简单使用

直接上代码:

public class ThreadLocaDemo {private static ThreadLocal<String> localVar = new ThreadLocal<String>();static void print(String str) {//打印当前线程中本地内存中本地变量的值System.out.println(str + " :" + localVar.get());//清除本地内存中的本地变量localVar.remove();}public static void main(String[] args) throws InterruptedException {new Thread(new Runnable() {public void run() {ThreadLocaDemo.localVar.set("local_A");print("A");//打印本地变量System.out.println("after remove : " + localVar.get());}},"A").start();Thread.sleep(1000);new Thread(new Runnable() {public void run() {ThreadLocaDemo.localVar.set("local_B");print("B");System.out.println("after remove : " + localVar.get());}},"B").start();}
}A :local_A
after remove : null
B :local_B
after remove : null

从这个示例中我们可以看到,两个线程分表获取了自己线程存放的变量,他们之间变量的获取并不会错乱。这个的理解也可以结合图1-1,相信会有一个更深刻的理解。

四、ThreadLocal的原理

要看原理那么就得从源码看起。

  4.1 ThreadLocal的set()方法:

 public void set(T value) {//1、获取当前线程Thread t = Thread.currentThread();//2、获取线程中的属性 threadLocalMap ,如果threadLocalMap 不为空,//则直接更新要保存的变量值,否则创建threadLocalMap,并赋值ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);else// 初始化thradLocalMap 并赋值createMap(t, value);}

 从上面的代码可以看出,ThreadLocal  set赋值的时候首先会获取当前线程thread,并获取thread线程中的ThreadLocalMap属性。如果map属性不为空,则直接更新value值,如果map为空,则实例化threadLocalMap,并将value值初始化。

那么ThreadLocalMap又是什么呢,还有createMap又是怎么做的,我们继续往下看。大家最后自己再idea上跟下源码,会有更深的认识。

  static class ThreadLocalMap {/*** The entries in this hash map extend WeakReference, using* its main ref field as the key (which is always a* ThreadLocal object).  Note that null keys (i.e. entry.get()* == null) mean that the key is no longer referenced, so the* entry can be expunged from table.  Such entries are referred to* as "stale entries" in the code that follows.*/static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}}

可看出ThreadLocalMap是ThreadLocal的内部静态类,而它的构成主要是用Entry来保存数据 ,而且还是继承的弱引用。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。详细内容要大家自己去跟。

//这个是threadlocal 的内部方法
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}//ThreadLocalMap 构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);}

4.2 ThreadLocal的get方法

    public T get() {//1、获取当前线程Thread t = Thread.currentThread();//2、获取当前线程的ThreadLocalMapThreadLocalMap map = getMap(t);//3、如果map数据不为空,if (map != null) {//3.1、获取threalLocalMap中存储的值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//如果是数据为null,则初始化,初始化的结果,TheralLocalMap中存放key值为threadLocal,值为nullreturn setInitialValue();}private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}

4.3 ThreadLocal的remove方法

 public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}

remove方法,直接将ThrealLocal 对应的值从当前相差Thread中的ThreadLocalMap中删除。为什么要删除,这涉及到内存泄露的问题。

实际上 ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。

所以如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。

ThreadLocal其实是与线程绑定的一个变量,如此就会出现一个问题:如果没有将ThreadLocal内的变量删除(remove)或替换,它的生命周期将会与线程共存。通常线程池中对线程管理都是采用线程复用的方法,在线程池中线程很难结束甚至于永远不会结束,这将意味着线程持续的时间将不可预测,甚至与JVM的生命周期一致。举个例字,如果ThreadLocal中直接或间接包装了集合类或复杂对象,每次在同一个ThreadLocal中取出对象后,再对内容做操作,那么内部的集合类和复杂对象所占用的空间可能会开始持续膨胀。

 4.4、ThreadLocal与Thread,ThreadLocalMap之间的关系  

图 Thread、THreadLocal、ThreadLocalMap之间啊的数据关系图

从这个图中我们可以非常直观的看出,ThreadLocalMap其实是Thread线程的一个属性值,而ThreadLocal是维护ThreadLocalMap

这个属性指的一个工具类。Thread线程可以拥有多个ThreadLocal维护的自己线程独享的共享变量(这个共享变量只是针对自己线程里面共享)

五、ThreadLocal 常见使用场景

如上文所述,ThreadLocal 适用于如下两种场景

1、每个线程需要有自己单独的实例

2、实例需要在多个方法中共享,但不希望被多线程共享

对于第一点,每个线程拥有自己实例,实现它的方式很多。例如可以在线程内部构建一个单独的实例。ThreadLoca 可以以非常方便的形式满足该需求。

对于第二点,可以在满足第一点(每个线程有自己的实例)的条件下,通过方法间引用传递的形式实现。ThreadLocal 使得代码耦合度更低,且实现更优雅。

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

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

相关文章

量子纠错--shor‘s 码

定理1 (量子纠错的条件) C是一组量子编码&#xff0c;P是映射到C上的投影算子。假设是一个算子元素描述的量子操作&#xff0c;那么基于量子编码C&#xff0c;存在一个能对抗描述的噪声的纠错操作R的充要条件是 对某个复元素厄米矩阵成立。 将算子元素称为导致的错误。如果这样…

[C++进阶数据结构]红黑树(半成品)

我们讲完了AVL树,它追求绝对平衡&#xff0c;从而导致插入和删除性能较差。今天我们来讲讲&#xff0c;红黑树&#xff0c;它是另一种平衡二叉搜索树&#xff0c;它追求相对平衡&#xff0c;使得增删查改的性能都极佳&#xff0c;时间复杂度皆为O(log2N)。 一、红黑树的概念 …

CSS3 动画相关属性实例大全(三)(columns、filter、flex、flex-basis 、flex-grow、flex-shrink属性)

CSS3 动画相关属性实例大全&#xff08;三) &#xff08;columns、filter、flex、flex-basis 、flex-grow、flex-shrink属性&#xff09; 本文目录&#xff1a; 一、columns属性&#xff08;设置元素的列宽和列数&#xff09; 二、filter属性&#xff08;调整图像、背景和边…

Ribbon客户端负载均衡策略测试及其改进

文章目录 一、目的概述二、验证步骤1、源码下载2、导入IDE3、运行前修改配置4、策略说明5、修改策略 三、最终结论四、改进措施1. 思路分析2. 核心代码3. 测试页面 一、目的概述 为了验证Ribbon客户端负载均衡策略在负载节点失效的情况下&#xff0c;是否具有故障转移的功能&a…

【逆向基础】十七、PE文件格式(二)

一、简介 本篇章主要PE文件组成部分中使用的结构体&#xff1b;根据结构体的成员变量去了解各个字节的含义。&#xff08;ps:我们依旧以”cmd.exe“为例展开解析&#xff1b;) 二、DOS Header 1、结构体&#xff1a;IMAGE_DOS_HEADER IMAGE_DOS_HEADER结构体的背景是为了兼…

忘记7-zip文件7-zip文件,还可以解压zip文件吗?

文件压缩与解压已成为我们日常处理数据和存储信息的常规操作。7-Zip&#xff0c;作为一款开源且功能强大的文件压缩工具&#xff0c;凭借其高压缩率、支持多种格式以及免费使用的特点&#xff0c;赢得了广大用户的青睐。然而&#xff0c;出于保护文件内容安全的考虑&#xff0c…

基于NVIDIA NIM平台—生成属于自己的DIY食谱

目录 一、介绍NVIDIA NIM平台 二、生成DIY食谱Demo 三、小结 一、介绍NVIDIA NIM平台 NVIDIA NIM&#xff08;Nvidia Inference Microservices&#xff09;平台是NVIDIA推出的一个微服务套件&#xff0c;旨在加速生成式AI模型在云端、数据中心和工作站上的部署和使用。以下是…

怎么区分主谓宾I love you与主系表I am fine? 去掉宾语看句子完整性 主系表结构则侧重于描述主语的状态、特征或性质

主谓宾与主系表是英语句子结构中的两种基本类型&#xff0c;它们在关注点、动词分类以及句子完整性方面有所区别。具体分析如下&#xff1a; 关注点 主谓宾I love you&#xff1a;主谓宾结构主要关注动作和影响对象之间的关系[1]。这种结构强调的是动态和行为&#xff0c;通常描…

4K双模显示器7款评测报告

4K双模显示器7款评测报告 HKC G27H7Pro 4K双模显示器 ROG华硕 XG27UCG 4K双模显示器 雷神 ZU27F160L 4K双模显示器 泰坦军团 P275MV PLUS 4K双模显示器 外星人&#xff08;Alienware&#xff09;AW2725QF 4K双模显示器 SANC盛色 D73uPro 4K双模显示器 ANTGAMER蚂蚁电竞 …

MySql中表的约束

​ 本篇中将会介绍关于 MySql 数据库中的表的约束&#xff0c;关于表的约束其实约束的是表中的数据类型&#xff0c;因为有的数据类型很单一&#xff0c;需要我们添加一些额外的约束&#xff0c;才能更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性&#xf…

Notepad++通过自定义语言实现日志按照不同级别高亮

借助Notepad的自定义语言可以实现日志的按照不同级别的高亮&#xff1b; 参考&#xff1a; https://blog.csdn.net/commshare/article/details/131208656 在此基础上做了一点修改效果如下&#xff1a; xml文件&#xff1a; <NotepadPlus><UserLang name"Ansibl…

leetCode算法题爬楼梯递归写法

题目&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2输出&#xff1a;2解释&#xff1a;有两种方法可以爬到楼顶。1. 1 阶 1 阶2. 2 阶 …

GPIO输入和输出

参考视频&#xff1a;2.1 [GPIO]4种输出模式_哔哩哔哩_bilibili 输出&#xff1a;通过写0或者写1&#xff0c;控制引脚输出低电压或高电压。 输入&#xff1a;通过读取引脚是0还是1&#xff0c;判断引脚输入的是高电压还是低电压。 输出 推挽开漏通用通用输出推挽通用输出开漏…

Asp.net Core MVC 动态路由

动态路由 asp.net core 3.0 就支持了 // 映射关系public class TranslationDatabase{private static Dictionary<string, Dictionary<string, string>> Translations new Dictionary<string, Dictionary<string, string>>{{"en", new Dictio…

yolo自动化项目实例解析(八)自建UI-键鼠录制回放

项目中关于键鼠的操作&#xff0c;不像我们之前自动化那样一步一步去定义的&#xff0c;而是用C写了一个记录键鼠的操作&#xff0c;通过回放的方法来实现的 一、通讯系统 1、创建websocket服务器 首先通过事件循环asyncio 和websockets&#xff0c;创建一个持久化的服务端进程…

通过页面添加国际化数据,实现vue的国际化

element ui 写在前面1. 原有的vue的国际化处理1.1 语言文件1.2 lang的index.js1.3 入口文件导入1.3 应用 2. 通过页面添加国际化数据2.1 做法2.2 lang的index.js文件修改2.3 需要注意的点 总结写在最后 写在前面 需求&#xff1a;在系统的国际化管理页面添加国际化数据&#x…

我想电脑批量管理 30 台苹果手机,怎么操作更简单方便呢?

在如今的数字化时代&#xff0c;手机已经成为了我们日常生活中不可或缺的一部分。无论是工作还是娱乐&#xff0c;我们都需要使用各种各样的应用软件来满足自己的需求。 而对于那些需要管理大量苹果手机设备的企业来说&#xff0c;如何高效地完成这些任务就成了一个重要问题。…

三款计算服务器配置→如何选择科学计算服务器?

科学计算在众多领域都扮演着关键角色&#xff0c;无论是基础科学研究还是实际工程应用&#xff0c;强大的计算能力都是不可或缺的。而选择一台合适的科学计算服务器&#xff0c;对于确保科研和工作的顺利进行至关重要。 首先&#xff0c;明确自身需求是重中之重。要仔细考虑计算…

六个方向比较分析:ChatGPT-o1-preview与 ChatGPT-4o在论文写作辅助上的差异

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 在学术研究和论文撰写的领域&#xff0c;人工智能助手正变得越来越重要。随着技术的不断进步&#xff0c;ChatGPT-o1-preview和ChatGPT-4o作为两个先进的语言模型&#xff0c;在辅助论文…

文件上传漏洞及安全

文件上传 文件上传安全指的是攻击者通过利用上传实现后门的写入连接后门进行权限控制的安全问题&#xff0c;对于如何确保这类安全问题&#xff0c;一般会从原生态功能中的文件内容&#xff0c;文件后缀&#xff0c;文件类型等方面判断&#xff0c;但是漏洞可能不仅在本身的代码…