ThreadLocal不仅要应付面试,更要真的理解,真的会用

前言

记得我几年前第一次面试的时候,就是被问了这个,记得面试官直接就让我说说ThreadLocal的实现原理以及平时有没有见过哪些地方用到了。
我当时初入职场,还是一个大菜鸟,所以直接就被干蒙了,至今还记忆犹新。

闲来无事,总结一下这块,其实仔细想想这个ThreadLocal,整体思路其实挺清晰的,但有些细节会有难度,可能会涉及到一些比较深的平时不用的知识,说实话我也还没有完全理清楚,但一直都在努力中。

概念

定义

我们说的ThreadLocal是java.lang包下的一个类,这个类提供特殊的线程局部变量,使得每个访问该变量的线程在其内部都有一个独立的初始化变量副本。
用人话解释:
先说普通类中定义的变量,我们都知道是多个线程共有的。
而ThreadLocal这个类中有个特殊的变量,特殊就特殊在针对不同的线程,在用这个ThreadLocal的时候,都能拿到本线程独有的值,你可以set,可以get,线程之间互不影响。

其实ThreadLocal这个概念,并不是java语言独有的,其实很多语言都有这个概念,只不过java中是用哈希表实现了这个概念。

特点

简单,开销小,线程安全。

哪里用ThreadLocal

1、Quartz的SimpleSemaphore,提供资源隔离

在这里插入图片描述
在这里插入图片描述
看上图:
SimpleSemaphore里面就有个方法:obtrainLock方法,用synchronized锁
这个方法中有个很重的while操作(消费者处理完所有事情,需要等待新的事情,这个等待是一个while循环)
lockName是这个方法的入参,这个while方法的判断逻辑是如果locks这个HashSet中有这个lockName,这个线程就执行wait()方法,由于obtrainLock本身是一个所方法,然后再去执行wait(),你的线程就被完全阻塞在这里排队了。
试想,如果没有ThreadLocal先过滤,那么同一个线程的多次调用这个obtrainLock方法,带着相同的lockName,就会多次进入这个while循环,其实同一个线程是不需要多次进入这个操作的
所以通过在这个加锁操作之前用ThreadLocal判断(isLockOwner方法),将同一个线程带着相同lockName调用这个方法的次数,就减少到一次了,即只会第一次进入while循环,其他的都被isLockOwner方法挡住了
最终使得访问后面很重的操作的频率大大降低,算是一个优化。

2、Mybatis的SqlSessionManager,资源持有

我们知道Mybatis连数据库后,会有个连接池,里面会维护有多个连接,每次操作数据库,都需要拿到连接,再去操作,拿连接就是那个sqlSession.getConnection方法,每次操作都可能拿到任何一个连接。
如果想要支持事务,那必须让一次事务的所有操作,都必须让同一个连接处理,这样才能要么一起成功,要么一起失败,而一次事务的每个操作都需要从线程池中拿连接,那如何保证一次事务的每次操作拿到的都是同一个连接呢?
一次事务的多个操作一般都是一个线程去执行的,那其实问题就变成如何保证一个线程拿到的总是相同的一个连接,这里就用到了ThreadLocal,将当前线程拿到的连接保存在ThreadLocal中,下次该线程拿连接,就直接从ThreadLocal中拿这个连接,这样就保证了同一个线程永远拿到同一个连接,而其他线程拿哪个连接不受这个线程的影响。

我们看看具体的代码实现:
先是定义ThreadLocal,存放的就是SqlSession,每一个连接对应一个SqlSession
在这里插入图片描述
然后开始将一个线程的SqlSession放入ThreadLocal中
在这里插入图片描述
真正用的时候,比如commit,rollback等方法,就都从ThreadLocal中获取连接了。
在这里插入图片描述

3、Spring的TransactionContextHolder

在这里插入图片描述
TransactionContext也叫分布式事务资源池,保存的是当前环境的上下文,里面有个PlatformTransactionManager,这个就是执行commit和rollback的类,所以在分布式事务中也要保住同一个线程用同一个PlatformTransactionManager去执行commit或rollback,所以最终TransactionContext用ThreadLocal保存起来,达到效果。

4、登录

登录的时候,可以把每个线程的登录信息放在ThreadLocal中,就保证了同一个人的操作始终在同一个线程中。

ThreadLocal核心源码解读

1、首先,每个Thread中,都有一个成员变量threadLocals

这个是专门为ThreadLocal加的,具体threadLocals的赋值过程,是在ThreadLocal中
threadLocals的类型是ThreadLocal.ThreadLocalMap,这个ThreadLocalMap是ThreadLocal中的自定义的一个内部map类,key是ThreadLocal对象,value是每个线程的那个独有的变量副本。
在这里插入图片描述

2、ThreadLocal的get方法

在这里插入图片描述
先拿到当前线程
getMap方法,就是从当前线程中拿ThreadLocalMap,这个就是Thread中那个成员变量。
ThreadLocalMap的key是当前这个ThreadLocal对象,value就是我们这个get方法真正要返回的值。
如果能拿到ThreadLocalMap,那么就返回ThreadLocalMap中当前ThreadLocal对象对应的value值。
如果拿不到ThreadLocalMap,就去初始化value,最后再返回value。

总结

我们看到ThreadLocal的实现,就能清楚的知道为什么ThreadLocal可以保存不同线程的不同值了。
是因为其实最终这些值还是保存在了各个线程中的一个map中,而ThreadLocal仅仅是作为这个map的一个key。
那么对于一个线程,如果他遇到多个ThreadLocal,其实线程中的那个map就有多对值了。
有没有一种反向操作的感觉,乍一看以为这些值都是保存在ThreadLocal中的,最终发现还是在线程中保存。

注意

要注意的是,每个线程中的ThreadLocalMap是ThreadLocal中定义的一个静态类,相当于ThreadLocal重写了一个map,那有人会问了,为什么不直接用HashMap呢?

其实这是一个涉及到java垃圾回收的问题,重写的这个ThreadLocalMap,主要就是为了这个事情搞的。

我们知道其实HashMap中真正的数据是在一个个Entry中的,其实ThreadLocalMap也是这样,只不过ThreadLocalMap中的Entry是继承了WeakReference这个类。我们知道ThreadLocalMap中的key值,其实是ThreadLocal对象,在set某个对象的时候,需要根据这个对象的hash值去hash表中找槽,如果找到对应的槽后,槽上原来的对象被回收了,那对于的hash表上的位置的值就是null,那么ThreadLocalMap就会对这种已经废弃掉的null值对应的槽做一些处理(主要是重新回收这些槽,并重新分配hash表大小等)。这样相当于同步了垃圾回收的结果。

这就是为什么要重写hashMap了,因为hashMap不会处理这些逻辑,不处理就会造成槽不断的被已经回收的ThreadLocal的空对象占用着释放不出来,最后影响hash的查找,因为时间久了,每次正常hash后应该放的槽都被null占了,只能继续向后移着放。

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

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

相关文章

秒杀业务的基础点

秒杀的是一个很常见的业务了。就是在某个时刻,让大量用户抢购少量的优惠的商品,从而达到商品曝光和电商网站的曝光,增大用户流量,从而提升整体销售额。 比如今年疫情下,各大电商网站,就针对口罩开展了秒杀…

随想,产品思维和开发思维

有时候,产品思维和开发思维,由于出发点的不同,会产生较大的分歧。 作为一个开发,不仅要有自己的思维,也要了解产品的思维,这样才能在和产品的撕逼的战斗中所向披靡,百战百胜。 举个例子&#x…

Base64编码的原理与常用实现

这篇主要是为了后面好介绍加密算法,做的铺垫。 这个是基础,什么是一个程序员的涵养,这些基础就是涵养。 平时可能用不到,但必须得会。 如果连这个原理都说不上来,就别玩王者荣耀绝地求生英雄联盟和平精英了&#xff0c…

MD5算法原理与常用实现

目录定义MD5特点常见应用场景1、校验文件的完整性2、存储用户密码原理1、填补信息2、拿到初始值3、真正的计算MD5为什么不可逆java实现和使用定义 MD全称Message-Digest,即信息摘要,所以MD家族的算法也叫信息摘要算法 MD家族有MD2、MD3、MD4、MD5&#…

SHA算法原理与常用实现

看本文前,最好先看看之前的这一篇关于MD5算法的介绍。 MD5算法原理与常用实现 本文目录定义MD5和SHA-1的碰撞问题常见应用场景1、类似MD5的应用场景2、比特币3、https签名算法会用到SHA-256算法原理1、填补信息2、拿到初始值3、真正的计算java实现和使用定义 SHA算…

MAC算法原理与常用实现

看本文前,最好先看看之前的对于MD5算法和SHA算法的介绍。 本文目录定义常见应用场景1、linux客户端:SecureCRT2、Google身份验证器3、银联pos机终端原理java实现和使用定义 MAC(Message Authentication Codes),是一种…

对称加密算法原理与常用实现

目录定义常用对称加密算法DES3DESAESPEB常用对称加密算法的java实现DES实现3DES实现AES实现PEB实现定义 原文通过加密秘钥生成密文,密文通过解密秘钥得到原文。 对于加密秘钥和解密秘钥是相同的算法,就叫对称加密算法。 常用对称加密算法 DES Data E…

面试 HTTP ,99% 的面试官都爱问这些问题

HTTP 和 HTTPS 的区别HTTP 是一种 超文本传输协议(Hypertext Transfer Protocol),HTTP 是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范HTTP 主要内容分为三部分,超文本(Hypertext)、传输…

非对称加密算法 --- RSA签名算法

目录RSA原理RSA应用场景RSA加密场景RSA签名场景RSA加解密和签名算法的java实现RSA原理 通过一定的规则,生成公钥和私钥,公钥和私钥总是成对出现。 公钥可以公开出去,任何人都可以知道。 私钥只有自己知道。 RSA算法能保证,公钥加…

@JsonFormat失效解决

先说结论 JsonFormat失效,换成JSONField就好了 问题经过和原理 JsonFormat作为Date类型的属性值,返回前端格式化处理,很方便。 比如: JsonFormat(pattern "yyyy/MM/dd HH:mm:ss")private Date updateTime;返回给前端…

如何在摆摊经济中脱颖而出

最近,摆摊经济开始火了起来,于是各路诸侯纷纷举起大旗开始摆摊。我周围也不乏有亲朋好友蠢蠢欲动,有的甚至已经初有规模。但这波摆摊风是否真的可行,对谁可行,有哪些风口,有哪些坑,我们慢慢分析…

java 实现 生成短链接服务

java实现短链接转换服务 类似上图这种短信,对应的就是一个短链接。 看到原理也不难,于是先写个最简易版的玩玩,以后有需求了再补充吧 下面是一个简易版的短链接生成代码 搭个spring-boot服务,复制这两个controller,就…

[转载] --- Fastjson1.2.68版及以下全版本远程代码执行漏洞通告

再这样&#xff0c;真的要放弃fastjson了 【安全通告】Fastjson <1.2.68全版本远程代码执行漏洞通告 尊敬的腾讯云用户&#xff0c;您好&#xff01;近日&#xff0c;腾讯云安全运营中心监测到&#xff0c;Fastjson <1.2.68版本存在远程代码执行漏洞&#xff0c;漏洞被利…

[记录] --- safari浏览器对于yyyy-MM的坑

问题 后端给前端返回的带格式的日期类型时&#xff0c;很多时候都是yyyy-MM-dd格式的&#xff0c;在一般浏览器中都没问题&#xff0c;但safari浏览器就会出问题。 解决&#xff1a; 换成yyyy/MM/dd格式

数据库选型经验汇总

数据库选型 下面这些都是免费开源的。 暂且不考虑数据量&#xff0c;大致的选型方案。 一般分析型数据库&#xff0c;都是可以支持GB到TB级别。 上面的分类都不是一定的&#xff0c;只是大体上的推荐。具体还得结合实际场景调整。 数据处理大致可以分成两大类&#xff1a; 联…

excel导出经验

excel导出经验&#xff0c;供参考 数据量&#xff1a;1万以下 直接导出&#xff0c;正常在5秒内 数据量&#xff1a;1万-10万 直接导出&#xff0c;正常在10秒内 数据量&#xff1a;超过10万&#xff0c;建议多文件导出。 可使用多线程查询&#xff0c;比如一个线程查1万…

LeetCode删除排序数组中的重复项(Java实现)

原题&#xff1a; 给定一个排序数组&#xff0c;你需要在 原地 删除重复出现的元素&#xff0c;使得每个元素只出现一次&#xff0c;返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 示例 1: …

关于高考报志愿的一些规划建议

目录前言1、报志愿&#xff0c;选大学中国大学的档次报志愿的整体思路不同分数线的报志愿建议超出一本分数线100分以上超过一本分数线50分左右到100分刚刚超过一本分数线不到50分二本分数线超很多&#xff0c;但刚好没上一本分数线刚上二本分数线的三本分数线专科分数线选择大学…

RabbitMQ和Kafka选型用哪个

作为一个有丰富经验的微服务系统架构师&#xff0c;经常有人问我&#xff0c;“应该选择RabbitMQ还是Kafka&#xff1f;” 基于某些原因&#xff0c; 许多开发者会把这两种技术当做等价的来看待。的确&#xff0c;在一些案例场景下选择RabbitMQ还是Kafka没什么差别&#xff0c…

spring boot controller 增加指定前缀的两种方法

1、增加配置 server.servlet.context-path: /api 这种是最常见的&#xff0c;加上这个配置后&#xff0c;所有的url&#xff0c;必须带上/api的前缀&#xff0c;才能访问到该url 2、过滤拦截 这种是加上/api也可以访问&#xff0c;不加/api也可以访问&#xff0c;适合项目重…