Java-ThreadLocal
Java-ThreadLocal
cmyang1. 简介
主要解决多线程中数据因并发产生不⼀致问题,同个线程共享数据,ThreadLocal为每⼀个线程都提供了变量的副本,使得每个线程在某时间访问到的并不是同⼀个对象,这样就隔离了多个线程对数据的数据共享,这样的结果是耗费了内存,但大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
ThreadLocal不能使用原子类型,只能使用Object类型
- 原理和源码
- ThreadLocal中的⼀个内部类ThreadLocalMap,这个类没有实现map接口,就是⼀个普通的Java类,但是实现的类似map的功能
- 每个数据用Entry保存,其中的Entry继承与WeakReference,用⼀个键值对存储,键为ThreadLocal的引用。
- 每个线程持有⼀个ThreadLocalMap对象,每⼀个新的线程Thread都会实例化⼀个ThreadLocalMap并赋值给成员变量threadLocals,使用时若已经存在threadLocals,则直接使用已经存在的对象。
2. 3种实现
2.1 ThreadLocal
2.2 InheritableThreadLocal
InheritableThreadLocal继承于ThreadLocal,主要解决父子线程变量传递问题,InheritableThreadLocal可以把父线程的变量传递给子线程。
1 | public class InheritableThreadLocal<T> extends ThreadLocal<T> { |
原理
- InheritableThreadLocal存储数据的在ThreadLocalMap的inheritableThreadLocals对象里,在线程初始化的时候,该对象会复制父线程的数据到子线程
- ThreadLocalMap存储数据在threadLocals中,不会复制父线程数据
存在的问题
- 使用线程池时失效,InheritableThreadLocal是在创建初始化线程的时候,进行赋值,但是线程池中的线程都是提前创建好的,所以无法复制线程变量
2.3 TransmittableThreadLocal
在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal
值的传递功能,解决异步执行时上下文传递的问题。
3. 问题
为什么ThreadLocal的键是弱引用,如果是强引用有什么问题
Java中除了基础的数据类型以外,其它的都为引用类型。而Java根据其生命周期的长短将引用类型又分为强引用 、 软引用 、 弱引用 、 虚引用,正常情况下我们平时基本上我们只用到强引用类型
强引用 new了⼀个对象就是强引用 Object obj = new Object();
软引用的生命周期比强引用短⼀些,通过SoftReference类实现,当内存空间足够,垃圾回收器就不会回收它; 当JVM认为内存空间不足时,就会去试图回收软引用指向的对象,也就是说在JVM抛出OutOfMemoryError之前,会去清理软引用对象主要用来描述⼀些【有用但并不是必需】的对象
使用场景:适合用来实现缓存,内存空间充足的时候将数据缓存在内存中,如果空间不足了就将其回收掉弱引用是通过WeakReference类实现的,它的生命周期比软引用还要短,在GC的时候,不管内存空间足不足都会回收这个对象
使用场景:⼀个对象只是偶尔使用,希望在使用时能随时获取,但也不想影响对该对象的垃圾收集,则可以考虑使用弱引用来指向该对象。如果是强引用,即使把ThreadLocal设置为null,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏
如果是弱引用,引用ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下⼀次ThreadLocalMap调用set、get、remove的时候会被清除。