Threadlocal的简单理解和部分源码分析

Mybatis-plus 可以实现字段自动填充功能,我通常会把多个表中共同字段抽离到了一个公共实体类中,如创建时间,创建人员,更新时间,更新人员等等,但是如何获取当前用户信息填入创建人员,更新人员字段?

ThreadLocal可以帮助我解决这个问题,我使用的的是JWT token 验证登陆状态,在每次Http请求时Header都会附带token,token中就会附带用户信息,当我们验证完后把用户信息set进Threadlocal,就可以在同一个线程中通过ThreadLocal获取用户信息。

ThreadLocal理解

图源自网络

ThreadLocal是线程安全的,它可以确保多线程访问时每个线程只能访问到自己的线程私有变量。它的线程隔离机制是通过把共享变量的副本存储到Thread.threadLocals实现,每个线程只能访问自己的副本,就能避免线程安全问题。

Thread类截图

ThreadLocal实际上就是一个操作Thread.threadLocals的外壳。Thread.threadLocals变量是一个keyvalueMap,Thread创建的时候默认初始化threadLocals为null,在ThreadLocal首次读或写的时候初始化threadLocals。ThreadLocalMap是ThreadLocal中的静态内部类,ThreadLocalMap通过Entry键值对的方式存储数据,是一个定制化的Map。

ThreadLocal源码分析

set()
1
2
3
4
5
6
7
8
9
10
11
12
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取Thread.threadLocals
ThreadLocalMap map = getMap(t);
//map不为空就设入键值对 key为当前ThreadLocal对象
if (map != null)
map.set(this, value);
else
//否则创建Thread.threadLocals
createMap(t, value);
}

我们可以发现ThreadLocal set的方法是对Thread.threadLocals这个定制化的Map进行操作

getMap()
1
2
3
4
ThreadLocalMap getMap(Thread t) {
//就是取出Thread.threadLocals
return t.threadLocals;
}
createMap()
1
2
3
4
void createMap(Thread t, T firstValue) {
//以当前TreadLocal对象为key 创建并初始化一个Thread.threadLocals
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
get()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取Thread.threadLocals
ThreadLocalMap map = getMap(t);
//map不为空 通过当前对象作为key 获取值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
//map部位空还得判断当前键在不在map里
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// map为空就创建并初始化
return setInitialValue();
}
setInitialValue()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   private T setInitialValue() {
//初始化value为null
T value = initialValue();
//获取当前线程
Thread t = Thread.currentThread();
//获取Thread.threadLocals
ThreadLocalMap map = getMap(t);
//判断map是否为空 不为空就把 key为当前对象 value为null 设置进map
if (map != null)
map.set(this, value);
else
//否则就创建并初始化Thread.threadLocals
createMap(t, value);
return value;
}

protected T initialValue() {
return null;
}
remove()
1
2
3
4
5
6
7
public void remove() {
//在当前线程里获取Thread.threadLocals
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
//在map里移除 以当前对象为key的 键值对
m.remove(this);
}

Threadlocal的简单理解和部分源码分析
https://cason.work/2022/06/16/Threadlocal的简单理解和部分源码分析/
作者
Cason Mo
发布于
2022年6月16日
许可协议