我们提供安全,免费的手游软件下载!
最近在阅读一个系统的代码时,发现系统中使用了ThreadLocal。乍一看,这似乎是一个很高级的用法。但仔细一看,发现这个场景并不存在线程安全问题,完全只是在一个方法中传参使用。经过和架构师请教和确认,发现这完全是一个ThreadLocal滥用的典型案例。甚至,日常的业务系统中,有90%以上的ThreadLocal都在滥用或错误使用。快来看看说的是不是你~
ThreadLocal,也称为线程局部变量,是Java提供的一个工具类,它为每个线程提供一个独立的变量副本,从而实现线程间的数据隔离。ThreadLocal中的关键方法包括get()、set(T value)和remove()。
在一些没有必要进行线程隔离的场景中使用“高级”的ThreadLocal,看起来是挺唬人的,但这其实就是“纸老虎”。滥用的典型案例是:在一个方法的内部,将入参信息写入ThreadLocal进行保存,在后续需要时从ThreadLocal中取出使用。这种滥用并不会对代码的运行结果产生影响,但是可能会在多线程处理时导致错误的结果。
以一个常见的Web应用为例,假设在Controller中使用ThreadLocal来保存线程中的用户信息,初识为null。业务逻辑很简单,先从ThreadLocal获取一次值,然后把入参中的uid设置到ThreadLocal中,随后再获取一次值,最后返回两次获得的uid。在默认情况下,这样的代码可能会得到正确的结果。但是,如果线程被复用,很可能从ThreadLocal获取的值是之前其他用户的遗留下的值,导致数据错乱。
数据错乱的原因在于程序运行在Tomcat中,Tomcat的工作线程是基于线程池的,线程池其实是复用了一些固定的线程。如果线程被复用,那么很可能从ThreadLocal获取的值是之前其他用户的遗留下的值。通过调整Tomcat的线程池参数,可以复现这个问题。解决办法是在finally代码块中显式清除ThreadLocal中的数据,这样即使复用了之前的线程,也不会获取到错误的用户信息。
正确的使用场景包括在网关场景下,使用ThreadLocal来存储追踪请求的ID、请求来源等信息;RPC等框架中使用ThreadLocal保存请求上下文信息。最常见的案例是用户登录拦截,从HttpServletRequest获取到用户信息,并保存到ThreadLocal中,方便后续随时取用。
本文介绍了ThreadLocal的滥用、错误使用的案例,分析了问题原因和解决方法,并给出了正确的使用案例。真正的高手往往使用最朴实无华的招数,写出无可挑剔的代码;有时候炫技式的代码可能会出错。大师级程序员把系统当作故事来讲,而不是当作程序来写。把故事讲好,即方便自己阅读,也方便别人阅读,共勉。
欢迎各位在评论区或者私信我一起交流讨论,或者加我主页weixin,备注技术渠道(如博客园),进入技术交流群,我们一起讨论和交流,共同进步!也欢迎大家关注我的博客园、公众号(码上暴富),点赞、留言、转发。你的支持,是我更文的最大动力!
相关资讯
热门资讯