这玩意比ThreadLocal叼多了,吓得why哥赶紧分享出来。 (1)

简介: 这玩意比ThreadLocal叼多了,吓得why哥赶紧分享出来。 (1)

从Dubbo的一次提交开始


故事得从前段时间翻阅 Dubbo 源码时,看到的一段代码讲起。



这段代码就是这个:


org.apache.dubbo.rpc.RpcContext


image.png


使用 InternalThreadLocal 提升性能。


相信作为一个程序猿,都会被 improve performance(提升性能)这样的字眼抓住眼球。


心里开始痒痒的,必须要一探究竟。


刚看到这段代码的时候,我就想:既然他是要提升性能,那说明之前的东西表现的不太好。


那之前的东西是什么?


image.png



经过长时间的推理、缜密的分析,我大胆的猜测到之前的东西就是:ThreadLocal。


来,带大家看一下:


image.png


果不其然,我真是太厉害了。


2018 年 5 月 15 日的提交:New threadLocal provides more performance. (#1745)


可以看到这次提交的后面跟了一个数字:1745。它对应一个 pr,链接如下:


https://github.com/apache/dubbo/pull/1745


在这个 pr 里面还是有很多有趣的东西的,出场人物一个比一个骚,文章的最后带大家看看。



能干啥用?


在说 ThreadLocal 和 InternalThreadLocal 之前,还是先讲讲它们是干啥用的吧。


InternalThreadLocal 是 ThreadLocal 的增强版,所以他们的用途都是一样的,一言蔽之就是:传递信息。


你想象你有一个场景,调用链路非常的长。当你在其中某个环节中查询到了一个数据后,最后的一个节点需要使用一下。


这个时候你怎么办?你是在每个接口的入参中都加上这个参数,传递进去,然后只有最后一个节点用吗?


可以实现,但是不太优雅。


你再想想一个场景,你有一个和业务没有一毛钱关系的参数,比如 traceId ,纯粹是为了做日志追踪用。


你加一个和业务无关的参数一路透传干啥玩意?


通常我们的做法是放在 ThreadLocal 里面,作为一个全局参数,在当前线程中的任何一个地方都可以直接读取。当然,如果你有修改需求也是可以的,视需求而定。


绝大部分的情况下,ThreadLocal 是适用于读多写少的场景中。


举三个框架源码中的例子,大家品一品。


第一个例子:Spring 的事务。


在我的早期作品《事务没回滚?来,我们从现象到原理一起分析一波》里面,我曾经写过:


Spring 的事务是基于 AOP 实现的,AOP 是基于动态代理实现的。所以 @Transactional 注解如果想要生效,那么其调用方,需要是被 Spring 动态代理后的类。


因此如果在同一个类里面,使用 this 调用被 @Transactional 注解修饰的方法时,是不会生效的。


为什么?


因为 this 对象是未经动态代理后的对象。


那么我们怎么获取动态代理后的对象呢?


其中的一个方法就是通过 AopContext 来获取。



image.png


其中第三步是这样获取的:AopContext.currentProxy();


然后我还非常高冷的(咦,想想就觉得羞耻)说了句:对于 AopContext 我多说几句。


看一下 AopContext 里面的 ThreadLocal:


image.png


调用 currentProxy 方法时,就是从 ThreadLocal 里面获取当前类的代理类。


那他是怎么放进去的呢?


我高冷的第二句是这样说的:


image.png


可以看到,经过一个 if 判断,如果为 true ,则调用 AopContext.setCurrentProxy 方法,把代理对象放到 AopContext 里面去。


而这个 if 判断的配置默认是 false,所以需要通过刚刚说的配置修改为 true,这样 AopContext 才会生效。


附送一个知识点给你,不客气。


image.png


第二个例子:mybatis 的分页插件,PageHelper。


使用方法非常简单,从官网上截个图:


image.png


这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上,也就是放在线程的 ThreadLocal 里面。


当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。


上面这个代码,应该写成下面这个样子:


image.png


这种写法,就能保证安全。


核心思想就一句话:只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。


因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。


就算代码在进入 Executor 前发生异常,导致线程不可用的情况,比如常见的接口方法名称和 XML 中的不匹配,导致找不到 MappedStatement ,由于自动清除,也不会导致 ThreadLocal 参数被错误的使用。


所以,我看有的人为了保险起见这样去写:


image.png


目录
相关文章
|
5月前
|
存储 安全 Java
《ThreadLocal使用与学习总结:》史上最详细由浅入深解析ThreadLocal
《ThreadLocal使用与学习总结:》史上最详细由浅入深解析ThreadLocal
46 0
|
6月前
|
监控 Java 测试技术
面试准备不充分,被Java守护线程干懵了,面试官主打一个东西没用但你得会
面试准备不充分,被Java守护线程干懵了,面试官主打一个东西没用但你得会
61 1
|
Java 应用服务中间件 Spring
ThreadLocal 的使用及踩坑
ThreadLocal 的使用及踩坑
92 1
面试又被问懵了吗?不如把ThreadLocal拆开了揉碎看看
1.为什么用 ThreadLocal? 所谓并发,就是有限资源需要应对远超资源的访问。解决问题的方法,要么增加资源应对访问;要么增加资源的利用率。 所以,相信这年头做开发的多多少少,都会那么几个“线程二三招”、“用锁五六式”。 那所带来的就是多线程访问下的并发安全问题。 共享变量的访问域跨越了原始的单线程,进入了千家万户的线程眼里。谁都可以用,谁都可以改,那不就打起来了吗? 因此,防止并发问题的最好办法,就是不要多线程访问(这科技水平倒退二十年~)。ThreadLocal 顾名思义,将一个变量限制为“线程封闭”:对象只被一个线程持有、访问、修改。
|
存储 安全 Java
|
存储 缓存 Java
终于弄明白了ThreadLocal
ThreadLocal是Thread的局部变量,用于编多线程程序,对解决多线程程序的并发问题有一定的启示作用。
160 0
终于弄明白了ThreadLocal
|
算法 安全 Java
threadlocal再温习
早时总结过《ThreadLocal解析》、《FastThreadLocal解析》 最近看一些资料的时候,又重重发现了这类,不希望再温下,许多知识点,之前已经总结了,这篇文章主要有两个问题: 1、弱引用的意义 2、如何防键冲突
243 0
threadlocal再温习
|
监控 C语言 iOS开发
小码哥底层原理之RunLoop
RunLoop是一个运行循环,保证App能够持续运行,处理各种事件,节省CPU资源,没事处理的时候就进入休眠。
172 0
小码哥底层原理之RunLoop
|
存储 安全 算法
面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(一)
在平时中集合使用中,当涉及多线程开发时,如果使用HashMap可能会导致死锁问题,使用HashTable效率又不高。而ConcurrentHashMap在保持同步同时并发效率比较高,ConcurrentHashmap是最好的选择,那面试中也会被常常问到,那可能的问题是:
417 0
面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(一)
|
存储 安全 算法
面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(二)
在上篇《面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(一)》,研究了基础原理,以及ConcurrentHashMap数据put的流程等线程安全的,来回顾一下面试的问题点
175 0
面试:为了进阿里,死磕了ConcurrentHashMap源码和面试题(二)