最近在接入易盾的时候,验证码等数据是通过过滤器获取,存储在ThreadLocal中,之后进行处理。ThreadLocal我接触较少,就查看了一些资料并整理。
首先需要知道线程局部变量。线程局部变量高效地为每个使用它的线程提供单独的线程局部变量值的副本,每个线程只能看到与自己相联系的值,而不知道别的线程可能正在使用或修改它们自己的副本,即线程局部变量。java是通过ThreadLocal类支持。
ThreadLocal 接口
get() 访问器检索变量的当前线程的值; set() 访问器修改当前线程的值。initialValue() 方法是可选的,如果线程未使用过某个变量,那么您可以用这个方法来设置这个变量的初始值;它允许延迟初始化。
ThreadLocal 的作用
常常是把有状态类描绘成线程安全的,或者封装非线程安全类以使它们能够在多线程环境中安全地使用的最容易的方式。使用ThreadLocal 使我们可以绕过为实现线程安全而对何时需要同步进行判断的复杂过程,而且因为它不需要任何同步,所以也改善了可伸缩性。除简单之外,用ThreadLocal 存储每线程单子或每线程上下文信息在归档方面还有一个颇有价值好处 — 通过使用 ThreadLocal ,存储在 ThreadLocal 中的对象都是不被线程共享的,是清晰的,从而简化了判断一个类是否线程安全的工作。
使用例子
查看源码
以set为例:
将Thread作为key,需要保存的对象作为value,保存在map中。每个线程拥有自己独立的ThreadLocal变量(指向ThreadLocalMap对象 ),每当线程访问 ThreadLocal变量时,访问的都是各自线程自己的ThreadLocalMap变量,从而做到线程安全。
设计ThreadLocal的初衷就是为了避免多个线程去并发访问同一个对象,尽管它是线程安全的。而在每个Thread中存放与它关联的ThreadLocalMap是完全符合ThreadLocal的思想的,当想要对线程局部变量进行操作时,只需要把Thread作为key来获得Thread中的ThreadLocalMap即可。这种设计相比采用一个全局Map的方法会多占用很多内存空间,但也因此不需要额外的采取锁等线程同步方法而节省了时间上的消耗。
ThreadLocal中的内存泄漏
ThreadLocalMap中的getEntry()、set()和remove()函数都会清理key为null的Entry,在使用线程池的情况下,如果不及时进行清理,内存泄漏问题事小,甚至还会产生程序逻辑上的问题。所以,为了安全地使用ThreadLocal,必须要像每次使用完锁就解锁一样,在每次使用完ThreadLocal后都要调用remove()来清理无用的Entry。