ThreadLocal原理
ThreadLocal原理
1. ThreadLocal简介
- ThreadLocal类提供了线程本地变量,每个线程都可以通过访问ThreadLocal对象获取到自己独立的变量副本,互不干扰。
- 线程可以通过
get()方法获取自己线程的本地副本,通过set()方法修改副本的值。 - 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class ThreadLocalExample {
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);
public static void main(String[] args) {
Runnable task = () -> {
int value = threadLocal.get();
value += 1;
threadLocal.set(value);
System.out.println(Thread.currentThread().getName() + " Value: " + threadLocal.get());
};
Thread thread1 = new Thread(task, "Thread-1");
Thread thread2 = new Thread(task, "Thread-2");
thread1.start(); // 输出: Thread-1 Value: 1
thread2.start(); // 输出: Thread-2 Value: 1
}
}
2. ThreadLocal原理
-
Thread类源码:1
2
3
4
5
6
7
8
9public class Thread implements Runnable {
//......
//与此线程有关的ThreadLocal值。由ThreadLocal类维护
ThreadLocal.ThreadLocalMap threadLocals = null;
//与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//......
} -
从源码中可以看出,
Thread类中有一个threadLocals和一个inheritableThreadLocals变量,类型均为ThreadLocal.ThreadLocalMap,默认值为null。只有当当前线程调用ThreadLocal的set()或get()方法时才会创建它们,实际上线程调用的是ThreadLocalMap类对应的get()和set()方法。 -
ThreadLocal的set()方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public void set(T value) {
//获取当前请求的线程
Thread t = Thread.currentThread();
//取出 Thread 类内部的 threadLocals 变量(哈希表结构)
ThreadLocalMap map = getMap(t);
if (map != null)
// 将需要存储的值放入到这个哈希表中
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
} -
通过代码可以看出:最终的变量是放在了当前线程的
ThreadLocalMap中,并不是存在了ThreadLocal对象中。ThreadLocal类中可以通过Thread.currentThread()获取到当前线程对象后,直接通过getMap(Thread t)获取该线程的ThreadLocalMap对象。 -
ThreadLocalMap以ThreadLocal对象作为键,以Object对象为value进行存储,每个Thread中都有一个ThreadLocalMap,每访问一个ThreadLocal对象,就会在当前线程的ThreadLocalMap中创建一个键值对。1
2
3ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
...
} -
ThreadLocal数据结构:

-
ThreadLocalMap是ThreadLocal的静态内部类。

2. ThreadLocal内存泄漏
ThreadLocal内存泄漏的根本原因在于其内部实现机制ThreadLocalMap的Entry定义如下:1
2
3
4
5
6
7
8static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}- 其中:
key(ThreadLocal 对象)是“弱引用”,也就是把ThreadLocal对象给丢了,JVM下轮GC就能把key给回收掉。value是“强引用”,也就是只要Thread对象还在,value就不会被回收掉。
- 当
key被GC回收掉后,Map里会出现key为null,但value不为null的情况,理想情况下,下次访问ThreadLocal或set/remove时,ThreadLocalMap会扫描并清理这些“僵尸key”,但是如果不在使用这个ThreadLocal,那么清理逻辑就永远不会触发,就会导致内存泄漏问题。 - 内存泄漏的发生需要同时满足两个条件:
ThreadLocal实例不再被强引用- 线程持续活动,导致
ThreadLocalMap长期存在
