定义
java
中ThreadLocal
的使用
ThreadLocal
主要用来为当前线程存储数据,这个数据只有当前线程可以访问。可以将ThreadLocal
看成是一个map
,而当前的线程就是map
中的key
。
下面是ThreadLocal
的类图结构,从图中可知:Thread
类中有两个变量threadLocals
和inheritableThreadLocals
,二者都是ThreadLocal
内部类ThreadLocalMap
类型的变量,我们通过查看内部内ThreadLocalMap
可以发现实际上它类似于一个HashMap
。在默认情况下,每个线程中的这两个变量都为null
,只有当线程第一次调用ThreadLocal
的set
或者get
方法的时候才会创建他们(后面我们会查看这两个方法的源码)。除此之外,和我所想的不同的是,每个线程的本地变量不是存放在ThreadLocal
实例中,而是放在调用线程的ThreadLocals
变量里面(前面也说过,该变量是Thread
类的变量)。也就是说,ThreadLocal
类型的本地变量是存放在具体的线程空间上,其本身相当于一个装载本地变量的工具壳,通过set
方法将value
添加到调用线程的threadLocals
中,当调用线程调用get
方法时候能够从它的threadLocals
中取出变量。如果调用线程一直不终止,那么这个本地变量将会一直存放在他的threadLocals
中,所以不使用本地变量的时候需要调用remove
方法将threadLocals
中删除不用的本地变量。
配置自定义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;
}
}