对ThreadLocal的一点了解

简介: ThreadLocal是线程变量,它为每个线程提供单独的存储空间。其主要作用是做线程间的数据隔离,也可以用于在同一个线程间方便地进行数据共享。(对于多线程资源共享,加锁机制采用“时间换空间”,ThreadLocal采用“空间换时间”)

ThreadLocal是线程变量,它为每个线程提供单独的存储空间。其主要作用是做线程间的数据隔离,也可以用于在同一个线程间方便地进行数据共享。(对于多线程资源共享,加锁机制采用“时间换空间”,ThreadLocal采用“空间换时间”)

原理:(自行参考ThreadLocal和Thread源码)

1、Thread类有两个成员变量threadLocals和inheritableThreadLocals,均为ThreadLocal类的静态内部类ThreadLocalMap类型的变量,ThreadLocalMap是一个类似于HashMap类型的容器类,默认情况下两个变量都为null,只有第一次调用ThreadLocal的get或set方法时才会创建。

2、threadLocal的set方法:获取当前线程t。获取t的成员变量threadLocals(ThreadLocal.ThreadLocalMap)并命名为map。map不为空则存入一个entry(Entry为ThreadLocal的静态内部类ThreadLocalMap的静态内部类并继承自WeakReference),entry的key为当前threadLocal(this)。map为空则初始化后存入entry。ThreadLocalMap和HashMap相像,它也靠计算key的哈希值来确定entry存放位置和判断两个entry是否相同,相同则会覆盖旧值。

区别在于:

ThreadLocalMap未实现Map接口,底层结构为Entry数组,且Entry继承自WeakReference(弱引用)。

HashMap的实现了Map接口,底层结构在jdk8后是数组、链表、红黑树。

HashMap解决哈希冲突的方式是在数组冲突的位置形成链表。ThreadLocalMap则是遇见冲突就按顺序寻找下一个位置存储。

3、threadLocal的get方法:获取当前线程成员变量threadLocals中当前threadLocal作为key对应的值,不存在则返回空。

4、threadLocal的remove方法:清除。remove方法不按预期调用时可能导致内存泄漏和各种bug。

附:

弱引用:只具有弱引用的对象拥有更短暂的生命周期,在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

ThreadLocal在没有外部强引用时会在GC时被回收,如果创建ThreadLocal的线程一直运行(如使用线程池时可能发生),则因Entry对象的value得不到回收从而发生内存泄露。

threadLocals和inheritableThreadLocals:创建线程时,构造函数中可以指定inheritThreadLocals参数来决定是否继承父线程的inheritableThreadLocals,从而实现父子线程间的数据传递。

使用场景:

Spring中,@Transaction的事务通过jdbc事务实现,管理某个事务时需要使用同一个数据库连接,该连接通过ThreadLocal来传递。采用ThreadLocal可以保证单个线程中的数据库操作使用的是同一个数据库连接。这种方式使得业务层使用事务时不需要感知和管理connection对象,通过传播级别,巧妙地管理多个事务配置之间的切换,挂起和恢复。

SimpleDataFormat在多线程共享使用时存在线程安全问题,除了使用DateTimeFormatter替代外,可以使用ThreadLocal包装SimpleDataFormat,再调用initialValue让每个线程有一个SimpleDataFormat的副本,从而解决线程安全问题,也提高了性能。

项目中经常存在在一个线程内横跨多个方法调用且需要传递一个对象(也就是上下文,可以理解为状态,比如用户身份信息)的场景,这时存在过渡传参的问题,如果给每个方法都增加context参数非常麻烦且不优雅。这时很适合使用ThreadLocal存放需要传递的参数。

PageHelper使用ThreadLocal支持线程隔离,保证并发访问时每个线程能够访问到各自独立的分页信息,并使用finally清除存储在ThreadLocal中的对象以确保下一次请求不会受到旧数据影响。(参考PageMethod、PageInterceptor、PageHelper)。

注意事项:PageHelper使用ThreadLocal保存分页参数,分页参数和线程是绑定的。因此需要保证PageHelper.startPage()调用后紧跟查询方法,否则可能导致非预期的方法进行分页。

还有很多场景的cookie,session等数据隔离是通过ThreadLocal实现的,比如spring对HttpServletRequest的管理(参考RequestContextHolder、ServletRequestAttributes)。

目录
相关文章
|
5月前
|
Java 测试技术 索引
ThreadLocal详解
文章详细讨论了Java中的`ThreadLocal`,包括它的基本使用、定义、内部数据结构`ThreadLocalMap`、主要方法(set、get、remove)的源码解析,以及内存泄漏问题和避免策略。`ThreadLocal`提供了线程局部变量,确保多线程环境下各线程变量的独立性,但不当使用可能导致内存泄漏,因此建议在不再需要`ThreadLocal`变量时调用其`remove`方法。
131 2
ThreadLocal详解
|
存储 算法 安全
深入详解ThreadLocal
在我们日常的并发编程中,有一种神奇的机制在静悄悄地为我们解决着各种看似棘手的问题,它就是 ThreadLocal 。
21533 9
深入详解ThreadLocal
|
缓存 安全 Java
浅谈ThreadLocal
浅谈ThreadLocal
159 0
|
存储 SQL Java
ThreadLocal的其他应用
request对象跟PageHelper
109 0
|
存储 Java
|
存储 Java
ThreadLocal相关使用
ThreadLocal相关使用
204 0
ThreadLocal相关使用
|
存储 Java
对threadlocal了解多少?
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。如果想实现每一个线程都有自己的专属本地变量该如何解决呢? JDK 中提供的 ThreadLocal 类正是为了解决这样的问题。 ThreadLocal 类主要解决的就是让每个线程绑定自己的值,可以将 ThreadLocal 类形象的比喻成存放数据的盒子,盒子中可以存储每个线程的私有数据。
|
存储 安全 Java
ThreadLocal 使用详解
ThreadLocal 是线程本地变量。当使用 ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程
568 0