彩色汽车驾驶模拟器
128.91M · 2026-04-08
ThreadLocal是Java中一个非常重要的线程封闭工具,它用于创建线程局部变量。每个线程都有自己独立初始化的变量副本,从而避免了多线程环境下的共享和同步问题。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
从这个方法能够看出,set方法是根据当前线程对象去取出一个ThreadLocalMap对象,实际的数据是放在这个ThreadLocalMap对象中的。键是ThreadLocal对象,值是传入的参数。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
从getMap这个方法可以看出,这个ThreadLocalMap对象是线程对象的一个数据对象。
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();
}
get方法依然是先获取线程中的ThreadLocalMap,然后以ThreadLocal对象为键,查找对应的值。
setInitialValue是一个兜底操作,放入一个初始化的值,并返回,默认是返回null,可以重写返回一个实际的值。
从上面可以看出,数据实际上是存在线程中的,当线程不退出的情况下,对象的引用将一致存在。
当线程退出的时候,会做一些清理操作,其中包括清理ThreadLocalMap
private void exit() {
if (threadLocals != null && TerminatingThreadLocal.REGISTRY.isPresent()) {
TerminatingThreadLocal.threadTerminated();
}
if (group != null) {
group.threadTerminated(this);
group = null;
}
/* Aggressively null out all reference fields: see bug 4006245 */
target = null;
/* Speed the release of some of these resources */
threadLocals = null;
inheritableThreadLocals = null;
inheritedAccessControlContext = null;
blocker = null;
uncaughtExceptionHandler = null;
}
因此,使用线程池的时候,当前线程会保留在线程池中,如果将一个比较大的对象设置在ThreadLocal中,可能会出现内存泄漏。
因此要及时回收对象,使用ThreadLocal对象的remove方法。
ThreadLocalMap中的Entry实现了弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
而且,值得注意的是,这个Entry与一般的Entry不同它仅有value,而没有key。
ThreadLocalMap中传入的键成为了一个弱引用对象。
为什么要将键设置成为一个弱引用对象?
这是为了避免内存泄漏,防止ThreadLocal对象本身的内存泄漏。
因为ThreadLocalMap是在线程中的,如果不使用弱引用,当外部的ThreadLocal强引用消失后,Entry这里还在强引用ThreadLocal对象,导致这个对象无法被GC。因此设计为弱引用,解决了键的泄漏问题。
ThreadLocal会出现内存泄漏,其根本原因在于ThreadLocalMap中的Entry中键是弱引用,而值是强引用。这里的泄漏是值泄漏。
在正常情况下:
ThreadLocal tl = new ThreadLocal();创建一个 tl对象。tl.set(value)存储数据。tl不再被需要时,将 tl变量置为 null(tl = null;)。null。key==null的条目(惰性清理)。在泄漏的情况(常见于线程池)::
tl变量被置为 null,堆中的 ThreadLocal 对象只被 Entry 的 Key 弱引用着。null,但 Value 依然被 Entry 强引用。ThreadLocalMap会一直存在。key=null的 Entry 及其 Value 对象,由于线程的强引用链(Thread -> ThreadLocalMap -> Entry -> Value)一直存在,只要线程存活,即使你再也无法通过任何代码访问到这个 Value(因为 Key 没了,get不到),它也无法被回收。这就造成了内存泄漏。泄漏主要是ThreadLocalMap的惰性清理没有触发。
128.91M · 2026-04-08
100.78M · 2026-04-08
112.05M · 2026-04-08