ThreadLocal
使用及其原理解析
一、前言
项目中使用到了SpringSecurity
框架作为安全验证,但是却发现一个问题,即当在子线程中获取SecurityContextHolder
中存储的对象时会报空指针异常,后来发现原来SpringSecurity
默认是将对象信息存储在ThreadLocal
类型的变量中,因此在子线程调用便会出现空指针异常,异常代码如下:
public void insertFill(MetaObject metaObject) {
log.info("创建时的自动插入策略生效");
this.strictInsertFill(metaObject, SystemConfig.createTime, Date.class, new Date());
if(loginUser == null){
try {
//此处会出现空指针异常
loginUser = (AdminLoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}catch (Exception e){
log.info("用户尚未登录,自动插入策略无需生效");
}
}
if (loginUser != null && loginUser.getAdminUser() != null){
this.strictInsertFill(metaObject, SystemConfig.createBy, String.class, loginUser.getAdminUser().getId());
}
}
SpringSecurity
使用ThreadLocal
存储上下文信息代码如下:
public static SecurityContext getContext() {
return strategy.getContext();
}
//默认采用就是ThreadLocal进行存储
private static final ThreadLocal<SecurityContext> contextHolder = new ThreadLocal<>();
public SecurityContext getContext() {
SecurityContext ctx = contextHolder.get();
if (ctx == null) {
ctx = createEmptyContext();
contextHolder.set(ctx);
}
return ctx;
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
因此,为了解决这一问题,打算研究一下ThreadLocal
使用及其原理
二、作用
1、基本概念
ThreadLocal
可以解释成线程的局部变量,也就是说一个ThreadLocal
的变量只有当前自身线程可以访问,别的线程都访问不了,那么自然就避免了线程竞争。因此,ThreadLocal
提供了一种与众不同的线程安全方式,它不是在发生线程冲突时想办法解决冲突,而是彻底的避免了冲突的发生
2、基本使用
创建一个ThreadLocal
对象:
public static void main(String[] args) {
ThreadLocal<String> localVar = new ThreadLocal<>();
localVar.set("hello world!!!"); //设置值为hello world!!!
String val = localVar.get(); //在当前线程取出对应的值
System.out.println(val); //输出结果为hello world!!!
//新开线程取ThreadLocal变量的值
Thread thread = new Thread(() -> {
System.out.println(localVar.get<