在使用 ThreadLocal 的时候,一般我们的代码都是这样写的:
public class ThreadLocalDemo {
private static final ThreadLocal<Map<String, Object>> context = ThreadLocal.withInitial(HashMap::new);
public static void setUserId(Long userId) {
context.get().put("userId", userId);
}
public static Long getUserId() {
return (Long) context.get().get("userId");
}
public static void remove() {
context.remove();
}
}
然后处理业务的是一个线程池,有一个结果就是 「ThreadLocal 引用」 context(静态变量) 和 「Thread 引用」(线程池里的线程)会常驻内存。引用关系如图所示:
虽然 key 使用了弱引用,但是 key 指向的 「ThreadLocal 对象」还一直被「ThreadLocal 引用」给引用着,这样 ThreadLocal 里的 expungeStaleEntry 方法是无法进行清理的(清理 key 为 null 的元素)。
ThreadLocal 的使用场景不应该图省事,就用来传递业务参数,这样增加了程序复杂度,代码可读性降低,处理不当还会内存泄漏,适用于一些日志、监控类的场景。
推荐的做法:
try {
ThreadLocalDemo.setUserId(124132523);
// 业务逻辑
} finally {
// 当前线程处理完数据以后清理掉 ThreadLocal 里面的数据,
// 处理业务的线程一般都是线程池,同一个线程可以处理多个请求,ThreadLocal 的数据是和请求相关的,及时清掉 ThreadLocal 里的数据,避免请求之间数据污染
ThreadLocalDemo.remove();
}
WeakReference:
软引用、弱引用、虚引用-他们的特点及应用场景
更多参考:
对ThreadLocal实现原理的一点思考