ThreadLocal---java
什么是ThreadLocal
ThreadLocal 是一种用于实现线程局部变量的工具类
。它允许每个线程都拥有自己的独立副本,从而实现线程隔离,避免了线程间的资源共享和同步问题。
使用 ThreadLocal
使用 ThreadLocal 通常分为四步:
public class UserHolder {
// 1.创建 ThreadLocal
private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();
// 2.设置 ThreadLocal 的值
public static void saveUser(UserDTO user){
tl.set(user);
}
// 3.获取 ThreadLocal 的值
public static UserDTO getUser(){
return tl.get();
}
// 4.删除 ThreadLocal 的值
public static void removeUser(){
tl.remove();
}
}
ThreadLocal 原理
在 Thread
类中有一个 ThreadLocalMap
类型的属性,也就是说每个线程都有自己的 ThreadLocalMap 实例。
ThreadLocal.ThreadLocalMap threadLocals = null;
- ThreadLocalMap 是 ThreadLocal 的静态内部类,它内部维护了一个
Entry
数组,key 是ThreadLocal
对象; - ThreadLocalMap 的大小是由 ThreadLocal 的数量决定的;
- Entry 继承了 WeakReference,它限定了 key 是一个
弱引用
,这样的好处就是当内存不足时,JVM 会回收 ThreadLocal 对象(无外部强引用),这样可以避免 ThreadLocal 对象本身的内存泄漏。 - 当调用 set、get 和 remove 方法时,首先会根据获取当前线程,然后可以得到属性 threadLocals,将 ThreadLocal 作为 key 查询对应的 value
ThreadLocal 内存泄露
我们前面说过,Entry.key 是弱引用,假设一种情况,ThreadLocal 对象无外部引用(如局部变量 tl 被回收),Entry.key 变为 null,但是ThreadLocalMap 是 Thread 的字段,只要线程不结束,ThreadLocalMap 和 Value 不会被回收
。Value 成为“僵尸对象”,占用内存但无法访问。
解决方案:实际应用中需要在使用完ThreadLocal变量后调用 remove()
方法释放资源
ThreadLocal 优化举例
Netty 中的 FastThreadLocal,它是 Netty 对 ThreadLocal 的优化,内部维护了一个索引常量 index,每次创建 FastThreadLocal 中都会自动+1,用来取代 hash 冲突带来的损耗,用空间换时间。
private final int index;
public FastThreadLocal() {
index = InternalThreadLocalMap.nextVariableIndex();
}
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
if (index < 0) {
nextIndex.decrementAndGet();
}
return index;
}