写在前面
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g.,a user ID or Transaction ID).
源码分析自 JDK 1.8.171
ThreadLocal 是一个用于暂存线程本地变量的工具数据结构类。
使用示例代码
1 | public class ThreadId { |
概述
ThreadLocal 能够存储当前线程可见的本地变量。在ThreadLocal中有一个 ThreadLocalMap
静态类,这个类才是具体存储变量的数据结构。在Thread中有一个声明为 ThreadLocal.ThreadLocalMap threadLocals = null;
的变量,这个变量就是存储的当前线程可见的本地变量,这个threadlocals由ThreadLocal类维护。
内部变量
private final int threadLocalHashCode = nextHashCode();
// ThreadLocal在线程Thread的hash值private static AtomicInteger nextHashCode = new AtomicInteger();
// 下一个ThreadLocal的hash值,一个线程可能会有多个ThreadLocalprivate static final int HASH_INCREMENT = 0x61c88647;
// 两个ThreadLocal之间hash差值
构造方法
public ThreadLocal()
// 默认构造方法
1 | /** |
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)
// 带初始值的构造方法
1 | public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) { |
initialValue方法是在调用get()方法的时候可能会调用的
**
ThreadLocalMap
ThreadLocalMap是一个hash map。这个map的 Entry
是在ThreadLocalMap内部的一个静态类,Entry
继承了 WeakReference<ThreadLocal<?>>
。
1 | static class ThreadLocalMap { |
源码分析
ThreadLocal的一般用法就是set和get方法,就从这两个方法作为入口分析一下逻辑流程。
公共逻辑
1 | ThreadLocalMap getMap(Thread t) { |
set
1 | public void set(T value) { |
get
1 | public T get() { |
总结
- ThreadLocal数据存储由一个ThreadLocalMap的数据结构完成,每个Thread类维护一个ThreadLocalMap
- ThreadLocalMap是一个Key为ThreadLocal的map,内部是由一个Entry[]数组存储元素,Entry继承WeakRefenrence<ThreadLocal<?>>
- 当一个Thread存在多个ThreadLocal时,ThreadLocalMap可能存储扩容的情况
- Thread类内部维护的ThreadLocalMap的初始化是lazy的,就是说只有当get或者set的时候,才会去初始化,并且允许带初始值(get的时候)
- ThreadLocalMap的Entry继承WeakReference<ThreadLocal<?>>,由它的类声明,可以理解为key是一个弱引用,但是value确实一个强引用,所以在一些情况下,key引用被回收了,value引用是未被回收的结,结合源码,在set、get和remove的时候都会涉及到包含这种情况的清理,试想在一段时间内,不涉及到set、get、remove操作,那么大概率还是存在内存泄漏的,所以一般建议是使用后手动remove以避免内存泄漏