前言
TheadLocal主要用于将似有线程和该线程存放的副本对象做一个映射,是的线程之间互不影响。
它为当前执行线程提供了一个副本,用于保存对象。使得线程之间都互不干扰。
结构
- 每个Thread内部都有一个Map
Map里面存储线程本地对象(key)和线程的变量副本(value)
使用
private static class NameHolder {private static ThreadLocal<String> LOCAL = new ThreadLocal<>();public static void set(String name) {LOCAL.set(name);}public static String get() {return LOCAL.get();}public static void remove() {LOCAL.remove();}}
建议:
- 尽量采取一个单独的类来保存
TheadLocal。 - 使用
public static进行修饰。
ThreadLocal
TheadLocal 保存来自于Thead对象。
public class Thread implements Runnable {ThreadLocal.ThreadLocalMap threadLocals = null;}
TheadLocal核心主要有get、set、remove方法。
public class ThreadLocal<T> {public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}}
ThreadLocalMap
ThreadLocal的内部类,独立实现了Map的功能。
主要使用Entry来保存K-V结构数据,Entry的key只能是ThreadLocal对象。
static class Entry extends WeakReference<ThreadLocal> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal k, Object v) {super(k);value = v;}}
一个线程如果需要需要存放多个值,定义多个TheadLocal对象即可,最后都会保存在TreadLocalMap当中的。
内存泄露

说明:
线程运行时,栈中存在当前 Thread 的栈帧,它持有 ThreadLocalMap 的强引用。ThreadLocal 所在的类持有一个 ThreadLocal 的强引用;同时,ThreadLocalMap 中的 Entry 持有一个 ThreadLocal 的弱引用。
场景一
线程正常执行消亡。那么ThradLocalMap引用不存在了。发生GC时,弱引用也会断开,整个ThreadLocalMap都会被回收。
场景二
如果是线程池的话,如果创建TheadLocal的线程一直持续运行,那么经过GC后,Entry持有的ThreadLocal引用断开,Entry的key为空,value不为空。当前线程一直持有TheadLocalMap,对象不会回收就产生内存泄露了。
变量属性private static
一般我们使用的都用都是定义为 private static 属性,如下:
private static ThreadLocal<String> LOCAL = new ThreadLocal<>();
这里对此此修饰有如下说明:
private修饰:使用private修饰是普遍问题,就当做是一个普通属性就好了。static修饰:这样做有好处,避免了错误产生,可以避免重复创建TSO(Thread Specific Object,即ThreadLocal所关联的对象)所导致的浪费;坏处是这样做可能正好形成内存泄漏所需的条件。
关键在于:static防止了无意义的多实例。
此时也有坏处,由于彩玉static,TheadLcalRef生命周期演成了,ThreadMap的key在线程中一直存在,导致得不到释放,必须手动释放处理。
InheritableThreadLocal
TheadLocal 只能绑定当前线程,如果当前线程的创建了子线程,那么访问不到TheadLocal,这时候就需要使用InheritableThreadLocal 处理了。
public class Thread implements Runnable {......(其他源码)/** 当前线程的ThreadLocalMap,主要存储该线程自身的ThreadLocal*/ThreadLocal.ThreadLocalMap threadLocals = null;/** InheritableThreadLocal,自父线程集成而来的ThreadLocalMap,* 主要用于父子线程间ThreadLocal变量的传递* 本文主要讨论的就是这个ThreadLocalMap*/ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;......(其他源码)}
核心实现
当前线程创建子线程时,会执行init方法,此时,子线程会将parentMap中的所有记录逐一复制到自身线程当中。
TransmittableThreadLocal
TransmittableThreadLocal 是Alibaba开源的、用于解决 “在使用线程池等会缓存线程的组件情况下传递ThreadLocal” 问题的 InheritableThreadLocal 扩展。 这里解释的比较清楚。主要为了解决线程池之间的传递问题。
GitHub地址:https://github.com/alibaba/transmittable-thread-local
使用
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>();context.set("value-set-in-parent");Runnable task = new RunnableTask();// 额外的处理,生成修饰了的对象ttlRunnableRunnable ttlRunnable = TtlRunnable.get(task);executorService.submit(ttlRunnable);// =====================================================// Task中可以读取,值是"value-set-in-parent"String value = context.get();
核心实现
主要就是引入了holder变量,再定义TransmittableThreadLocal时,同时初始了holder。TtlRunnable包裹Runnable时,会将此值带过去。

