@[toc]
1. 简介
线程同步除了使用锁机制外,还可以使用ThreadLocal来避免线程不安全的出现。ThreadLocal提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有一个该变量的一个副本。在实际的多线程操作时,每个线程操作的都是自己本地内存中的变量,彼此之间不会出现干扰,从而避免了线程安全问题的出现。

2. 案例
public class ThreadLocalDemo {private static ThreadLocal<String> local = new ThreadLocal<>();public static void main(String[] args) {new Thread(() -> {local.set("local value1");print("thread1");System.out.println("after remove:" + local.get());}).start();new Thread(() -> {// 设置当前线程中本地内存的变量值local.set("local value2");print("thread2");System.out.println("after remove:" + local.get());}).start();}public static void print(String str){// 获取当前线程它本地内存中的变量值System.out.println(str + ":" + local.get());// 清除本地内存中的本地变量local.remove();}}
thread1:local value1thread2:local value2after remove:nullafter remove:null
3. Thread类
Thread的类图如下所示:

Thread的源码定义为:
publicclass Thread implements Runnable {/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;/** InheritableThreadLocal values pertaining to this thread. This map is* maintained by the InheritableThreadLocal class.*/ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;}
Thread类的成员变量中定义了两个ThreadLocal.ThreadLocalMap类型的变量threadLocals和inheritableThreadLocals,并且初始化都是null,只有当第一次调用ThreadLocal的set()或get()时才会创建。threadLocals用于存放每个线程的本地变量,set()会将value添加到调用线程的threadLocals中,通过get()可以获取保存的变量。只要调用线程不终止,threadLocals中的变量会一直存在,直到调用remove()主动的删除变量。
4. ThreadLocal类
下面我们接着看一下ThreadLocal类的源码,重点关注其中比较重要的几个方法:
public class ThreadLocal<T> {protected T initialValue() {return null;}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();}private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}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 getMap(Thread t) {return t.threadLocals;}void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);}}
而其中使用的ThreadLocalMap是ThreadLocal中定义的静态内部类。
4.1 set()
ThreadLocal中set()的源码为:
public void set(T value) {// 获取当前线程Thread t = Thread.currentThread();// 获取Thread的成员变量ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 如果当前map为null,则将value保存在map中if (map != null)map.set(this, value);else// 否则,懒加载模式创建map,然后将当前线程变量和value放到map中createMap(t, value);}
其中getMap()的实现为:
ThreadLocalMap getMap(Thread t) {// 获取线程自己的threadLocals变量,并绑定到调用线程的threadLocals上return t.threadLocals;}
方法的返回值就是每个线程的threadLocals变量,它就是ThreadLocalMap类型的对象。如果map不为空,调用set()直接存放到threadLocals中,key就是当前线程的引用,值为传入的value;否则调用createMap(),它的源码为:
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}
它会新建一个ThreadLocalMap对象,并将其作为当前调用线程的threadLocals变量的初始化值。
4.2 get()
get()的源码为:
public T get() {// 获取当前线程Thread t = Thread.currentThread();// 获取当前线程的threadLocals变量ThreadLocalMap map = getMap(t);if (map != null) {// 获取当前线程的Entry对象ThreadLocalMap.Entry e = map.getEntry(this);// 如果Entry对象不为null,则获取保存的值if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}// 否则初始化当前线程的threadLocals变量return setInitialValue();}
setInitialValue()的源码实现为:
private T setInitialValue() {// threadLocals初始为nullT value = initialValue();// 获取当前线程Thread t = Thread.currentThread();// 查找当前线程引用对应的线程变量ThreadLocalMap map = getMap(t);// 如果threadLocals不为null,直接添加本地变量// key为当前线程引用,value为本地变量值if (map != null)map.set(this, value);else// 创建对应的threadLocals变量createMap(t, value);return value;}
protected T initialValue() {return null;}
4.3 remove()
remove()的源码为:
public void remove() {//获取当前线程绑定的threadLocalsThreadLocalMap m = getMap(Thread.currentThread());// 如果线程的threadLocals变量不为null,则直接删除if (m != null)m.remove(this);}
4.4 不支持继承性
同一个ThreadLocal变量在父线程中被设置值后,在子线程中是获取不到的
public class ThreadLocalDemo2 {private static ThreadLocal<String> local = new ThreadLocal<>();public static void main(String[] args) {local.set("main value");new Thread(() ->System.out.println(Thread.currentThread().getName() + " : " + local.get())).start();System.out.println(Thread.currentThread().getName() + " : " + local.get());}}
main : main valueThread-0 : null
从输出中可以看出,主线程和自定义线程各自本地内存中的变量是不同的,这再一次验证了ThreadLocal的原理。
5. InheritableThreadLocal类
InheritableThreadLocal类实现了子线程可以访问父线程的本地变量,首先改变下上面的代码,看下效果:
public class ThreadLocalDemo3 {private static InheritableThreadLocal<String> local = new InheritableThreadLocal<>();public static void main(String[] args) {local.set("main value");new Thread(() ->System.out.println(Thread.currentThread().getName() + " : " + local.get())).start();System.out.println(Thread.currentThread().getName() + " : " + local.get());}}
main : main valueThread-0 : main value
那它是如何实现子线程来访问父线程的本地变量呢?首先看下InheritableThreadLocal类的源码:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {protected T childValue(T parentValue) {return parentValue;}ThreadLocalMap getMap(Thread t) {return t.inheritableThreadLocals;}void createMap(Thread t, T firstValue) {t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);}}
InheritableThreadLocal继承了ThreadLocal类,并添加了自己的三个方法childvalue()、getMap()和createMap()。
5.1 createMap()
createMap()方法这里new的同样是ThreadLocalMap对象,但获取的对象是作为inheritableThreadLocals的初始值。
5.2 getMap()
getMap()这里获取的的就是当前线程的inheritableThreadLocals变量。
5.3 childvalue()
childvalue()来获取父线程的本地变量值。那它是如何实现的呢?理解实现的过程,首先需要看Thread类中的init()的源码 :
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {init(g, target, name, stackSize, null, true);}
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {if (name == null) {throw new NullPointerException("name cannot be null");}this.name = name;// 获取当前线程Thread parent = currentThread();// 安全校验SecurityManager security = System.getSecurityManager();if (g == null) {if (security != null) {g = security.getThreadGroup();}if (g == null) {g = parent.getThreadGroup();}}g.checkAccess();if (security != null) {if (isCCLOverridden(getClass())) {security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);}}g.addUnstarted();this.group = g;this.daemon = parent.isDaemon();this.priority = parent.getPriority();if (security == null || isCCLOverridden(parent.getClass()))this.contextClassLoader = parent.getContextClassLoader();elsethis.contextClassLoader = parent.contextClassLoader;this.inheritedAccessControlContext =acc != null ? acc : AccessController.getContext();this.target = target;setPriority(priority);// 如果父线程的inheritableThreadLocal不为nullif (inheritThreadLocals && parent.inheritableThreadLocals != null)// 设置子线程中的inheritableThreadLocals为父线程的inheritableThreadLocalsthis.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);this.stackSize = stackSize;tid = nextThreadID();}
如果父线程inheritableThreadLocal不为null,则通过createInheritedMap()将父线程的inheritableThreadLocals作为构造函数的参数创建一个新的ThreadLocalMap变量,然后赋给子线程。
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);}private ThreadLocalMap(ThreadLocalMap parentMap) {Entry[] parentTable = parentMap.table;int len = parentTable.length;setThreshold(len);table = new Entry[len];for (int j = 0; j < len; j++) {Entry e = parentTable[j];if (e != null) {@SuppressWarnings("unchecked")ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();if (key != null) {//调用重写的方法Object value = key.childValue(e.value);Entry c = new Entry(key, value);int h = key.threadLocalHashCode & (len - 1);while (table[h] != null)h = nextIndex(h, len);table[h] = c;size++;}}}}
在构造函数中将父线程的inheritableThreadLocals成员变量的值赋值到新的ThreadLocalMap对象中,返回之后赋值给子线程的inheritableThreadLocals。总之,InheritableThreadLocals类通过重写getMap和createMap两个方法将本地变量保存到了具体线程的inheritableThreadLocals变量中,当线程通过InheritableThreadLocals实例的set或者get方法设置变量的时候,就会创建当前线程的inheritableThreadLocals变量。而父线程创建子线程的时候,ThreadLocalMap中的构造函数会将父线程的inheritableThreadLocals中的变量复制一份到子线程的inheritableThreadLocals变量中。
6. ThreadLocalMap
ThreadLocalMap的类图如下所示:

实现源码:
static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {// value就是和ThreadLocal绑定的Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}...}
ThradLocalMap类内部又定义了一个静态内部类Entry,它类似于Map集合中的Entry对象,类型为键值对形式,真正起到了ThradLoacalMap存放数据的能力。Entry本身继承了弱引用WeakReference类,因此键值对的key为ThreadLocal的弱引用,value为ThreadLocal的set()传入的value。

WeakReference的源码为:
public class WeakReference<T> extends Reference<T> {public WeakReference(T referent) {super(referent);}public WeakReference(T referent, ReferenceQueue<? super T> q) {super(referent, q);}}
而WeakReference又继承了Reference,它是一个抽象类,所有类型的引用都是它的子类。
弱引用的特点简单来说就是发现即回收,这就导致了ThreadLocal可能会造成内存泄漏,这如何理解呢?ThreadLocal存放数据依赖于ThreadLocalMap这个内部类,而ThreadLocalMap又依赖于它的静态内部类Entry,Entry对象的键为当前TreadLocal的弱引用this,值为通过set()传入的value。因此,当Java虚拟机发现了这个弱引用后,垃圾收集器执行GC时就会对其进行回收,而这个弱引用和value并不是同生共死的。虽然key被回收了,但只要当前线程一直存在且没有调用ThreadLocal的remove(),value就会一直存在于内存空间中,但是失去了key再也无法获取到它,此时就发生了内存泄漏。
关于不同引用之间的区别,可查看:垃圾回收相关概念 + 引用分析
