ThreadLocal的理解和使用
理解
ThreadLocal是一种用于在多线程环境中存储线程局部变量的机制,它为每个线程都提供了独立的变量副本,从而避免了线程之间的竞争条件,事实上ThreadLocal的工作原理是在每个线程中创建一个独立的变量副本,并且每个线程只能访问自己的副本。ThreadLocal一个最广泛的使用场景就是将信息保存,从而方便后续方法直接从线程中获取。
ThreadLocal核心方法
方法声明 | 描述 |
---|---|
public void set(T value) | 设置当前线程绑定的局部变量 |
public T get() | 获取当前线程绑定的局部变量 |
Public void remove() | 移除当前线程绑定的局部变量 |
TheadLocal使用
public class LoginUserContextHolder {
/**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();
/**
* 设置变量
*/
public static void set(LoginUser user) {
threadLocal.set(user);
}
/**
* 获得变量
*/
public static LoginUser get() {
return threadLocal.get();
}
/**
* 清理当前变量
*/
public static void remove() {
threadLocal.remove();
}
}
实际例子: 前端使用token令牌调用后端接口,后端在Filter(过滤器)中拦截token令牌,然后判断token是否有效可用,从而可获取登录时存储在Redis中的用户信息,然后通过上述方法将登录用户信息放入到ThreadLocal中,那么在执行到接口层就可通过ThreadLocal获取到当前登录用户信息。
注意点
使用完后要进行手动释放,要不然会造成内存泄漏,也可能导致意外的数据污染或不一致性。
内存泄漏:上述threadLocal对象,若没有手动删除当前线程的用户信息,每次请求执行接口时都会创建一个不同的线程,都会将用户信息放到threadLocal对象中,而threadLocal的生命周期相当于应用系统的生命周期,GC无法回收,这样就会导致执行接口次数越多,内存泄漏得越多。
数据污染或不一致性:上述threadLocal对象,若没有手动删除当前线程的用户信息,可能会导致不同的token令牌请求(用户不同)会得到同一个用户信息。主要原因是使用线程池,线程池是拥有核心线程数和最大线程数的,核心线程会一直存在那里不会销毁,核心线程外的最大线程是有一定的存活时间,那么就有可能当前接口的线程就存活在线程池中,从而得到错误的用户信息。