定义
java
中ThreadLocal
的使用
ThreadLocal
主要用来为当前线程存储数据,这个数据只有当前线程可以访问。可以将ThreadLocal
看成是一个map
,而当前的线程就是map
中的key
。
下面是ThreadLocal
的类图结构,从图中可知:Thread
类中有两个变量threadLocals
和inheritableThreadLocals
,二者都是ThreadLocal
内部类ThreadLocalMap
类型的变量,我们通过查看内部内ThreadLocalMap
可以发现实际上它类似于一个HashMap
。在默认情况下,每个线程中的这两个变量都为null
,只有当线程第一次调用ThreadLocal
的set
或者get
方法的时候才会创建他们(后面我们会查看这两个方法的源码)。
除此之外,和我所想的不同的是,每个线程的本地变量不是存放在ThreadLocal
实例中,而是放在调用线程(这里指的是调用set方法的线程)的ThreadLocals
变量里面(前面也说过,该变量是Thread
类的变量)。也就是说,ThreadLocal
类型的本地变量是存放在具体的线程空间上,其本身相当于一个装载本地变量的工具壳,通过set
方法将value
添加到调用线程的threadLocals
中,当调用线程调用get
方法时候能够从它的threadLocals
中取出变量。如果调用线程一直不终止,那么这个本地变量将会一直存放在他的threadLocals
中,所以不使用本地变量的时候需要调用remove
方法将threadLocals
中删除不用的本地变量。
set
的时候是先拿到当前线程的ThreadLocal.ThreadLocalMap threadLocals
变量,然后在变量中put threadLocal
对象和对应的key
get
的时候是先拿到当前线程的ThreadLocal.ThreadLocalMap threadLocals
变量,然后再通过自己定义的threadLocal
对象get
到对应的值.(因为一个thread
可能有多个threadLocal
所以用threadLocal
对象做key
比较合适)
下图中展示的就很形象
配置自定义ThreadLocal
在拦截器或者过滤器中set
完之后再请求完成之后一定要remove
,不然可能会数据错乱
package com.felix.spring_cloud_one.threadLocal;
/**
* @author fchen
*/
public class RequestContext {
private final static ThreadLocal<Object> my = ThreadLocal.withInitial(() -> new Object());
public static void set(Object data){
my.set(data);
}
public static Object get(){
return my.get();
}
public static void remove(){
my.remove();
}
}
配置拦截器
当请求url
错误的时候,比如404
,此时会进入两次重写的preHandle
方法,因为第一次请求发现404
后会转到/error
路径
package com.felix.spring_cloud_one.threadLocal.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.felix.spring_cloud_one.threadLocal.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author fchen
*/
@Component
public class HttpInterceptor extends HandlerInterceptorAdapter {
private final AtomicInteger atomicLong = new AtomicInteger(0);
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("上一次的threadLocal数据{}" + RequestContext.get().toString());
Map<String,Object> map = new HashMap<>();
map.put("currentThread", Thread.currentThread().toString());
map.put("atomicLong", atomicLong.get());
System.out.println("当前atomicLong: " + atomicLong.getAndIncrement());
System.out.println("请求路径:" + request.getRequestURI());
RequestContext.set(map);
System.out.println("添加自定义threadLocal数据{}" + objectMapper.writeValueAsString(RequestContext.get()));
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("移除自定义threadLocal数据{}" + objectMapper.writeValueAsString(RequestContext.get()));
RequestContext.remove();
}
}
注册拦截器
package com.felix.spring_cloud_one.threadLocal.config;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 配置拦截器
* @author fchen
*/
@Configuration
public class ThreadLocalAdapter implements WebMvcConfigurer , ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry
.addInterceptor(applicationContext.getBean(HttpInterceptor.class))
//拦截所有路径
//.addPathPatterns("/**")
;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}