ThreadLocal类主要解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存储每个线程的私有数据。
1.使用demo
比较简单的存取
public class ThreadLocalTest1 {
public static void main(String[] args) {
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
ThreadLocal<Integer> threadLocal1 = new ThreadLocal<>();
threadLocal.set(1);
threadLocal1.set(2);
System.out.println("threadLocal=" + threadLocal.get());
System.out.println("threadLocal1=" + threadLocal1.get());
}
}
运行结果
threadLocal=1
threadLocal1=2
2.实战
当前用户。绑定当前登录的用户到http request线程,在拦截器赋值,在业务代码中获取使用。
只要不新开一个线程,那么在controller和service层就可以直接MingSanContext.getCurrentUser()
获取到当前登录的用户信息。
如果想在子线程也能获取到,在项目源码中获取方案,项目地址在文章末尾。
注意:一定要remove,不然会内存泄漏,甚至溢出。
public class MingSanContext {
private static final ThreadLocal<CurrentUser> LOGIN_THREAD_LOCAL = new ThreadLocal<>();
public static void setCurrentUser(CurrentUser currentUser) {
if (currentUser != null) {
LOGIN_THREAD_LOCAL.set(currentUser);
}
}
/**
* 获取当前访问用户
* <p>
* 返回CurrentUser这个对象理论上不会为空,里面的参数可能为null,
* <p>
* 但是网关也有拦截
* <p>
* 建议 CurrentUser可以不判断,currentUser的参数要判断
*
* @return 用户信息
*/
public static CurrentUser getCurrentUser() {
return LOGIN_THREAD_LOCAL.get();
}
/**
* 清除当前用户信息
*/
public static void removeCurrentUser() {
LOGIN_THREAD_LOCAL.remove();
}
}
3.原理
一切皆在Thread类中。
Thread类包含两个ThreadLocal.ThreadLocalMap
。默认的构造方法,会初始化inheritableThreadLocals
,不会初始化threadLocals
。
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
再看ThreadLocal类的set方法,获取当前线程的ThreadLocalMap Thread.threadLocals
。第一次获取肯定是null,因为new Thread()不会初始化threadLocals
,调用createMap方法,给当前线程的threadLocals
赋值。ThreadLocalMap的key是当前ThreadLocal对象,值是调用方set的值。
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
}
4.注意
ThreadLocal只可以在当前线程中使用,子线程中获取不到值。
public class ThreadLocalTest2 {
public static void main(String[] args) {
ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
threadLocal.set(1);
System.out.println("当前线程获取ThreadLocal值:" + threadLocal.get());
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程获取ThreadLocal值:" + threadLocal.get());
}
}).start();
}
}
运行结果
当前线程获取ThreadLocal值:1
子线程获取ThreadLocal值:null
ThreadLocal实战,源码地址:highway-resources: threadlocal-current-user
关注公众号- 编程highway - 获取更多好文和学习资料