父子线程间传值问题以及在子线程或者异步情况下使用RequestContextHolder.getRequestAttributes()的注意事项和解决办法

用到的工具类:

java 复制代码
@Slf4j
@Configuration
@Lazy(false)
public class SpringContextUtil{
  public static HttpServletRequest getRequest() {
    ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    if (servletRequestAttributes == null) {
        throw new RuntimeException("无法获取servletRequestAttributes ");
    } else {
        return servletRequestAttributes.getRequest();
    }
 }

}

 

第一种情况(子线程无法获取到父线程的值):

java 复制代码
    @GetMapping("/threadValueTransfer1")
    private void ThreadValueTransfer1(){
        log.info("开始主线程:{},打印请求头信息--》{}",Thread.currentThread(),SpringContextUtil.getRequest().getHeader("age"));
        new Thread(()->{
            String age = SpringContextUtil.getRequest().getHeader("age");
            log.info ("开始子线程:{},打印请求头信息--》{}",Thread.currentThread(),age);
        }).start();
    }

结果:

解决办法:

java 复制代码
  @GetMapping("/threadValueTransfer2")
    private void ThreadValueTransfer2() throws InterruptedException {
        log.info("开始主线程:{},打印请求头信息--》{}",Thread.currentThread(),SpringContextUtil.getRequest().getHeader("age"));
        RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
        new Thread(()->{
            String age = SpringContextUtil.getRequest().getHeader("age");
            log.info ("开始子线程:{},打印请求头信息--》{}",Thread.currentThread(),age);
        }).start();
    }

使用RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);开启父子线程间通信

第二中情况(主线程结束,子线程无法获取到值):

java 复制代码
  @GetMapping("/threadValueTransfer3")
    private void threadValueTransfer3() throws InterruptedException {
        log.info("开始主线程:{},打印请求头信息--》{}",Thread.currentThread(),SpringContextUtil.getRequest().getHeader("age"));
        RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
        new Thread(()->{
            try {
                Thread.sleep(3*1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            String age = SpringContextUtil.getRequest().getHeader("age");
            log.info ("开始子线程:{},打印请求头信息--》{}",Thread.currentThread(),age);
        }).start();
    }

结果:

解决(使用阿里的ttl)

步骤:

1.拦截器拦截将请求头中信息放到TransmittableThreadLocal中

2.注册拦截器

3.使用

依赖:

java 复制代码
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.6.0</version>
        </dependency>

TransmittableThreadLocal上下文工具类

java 复制代码
public class AgeContext {

    private static final ThreadLocal<String> AGE = new TransmittableThreadLocal<>();

    public static String get() {
        return AGE.get();
    }

    public static void set(String age) {
        AGE.set(age);
    }

    public static void clean() {
        if (AGE.get() != null) {
            AGE.remove();
        }
    }

}

拦截器

java 复制代码
public class RequestInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String age = request.getHeader("age");
        AgeContext.set(age);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        AgeContext.clean();
    }
}

注册:

java 复制代码
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login", "/register");
    }
}

使用:

java 复制代码
 @GetMapping("/threadValueTransfer4")
    private void threadValueTransfer4() throws InterruptedException {
        log.info("开始主线程:{},打印请求头信息--》{}",Thread.currentThread(),AgeContext.get());
        new Thread(()->{
            try {
                Thread.sleep(3*1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            String age = AgeContext.get();
            log.info ("开始子线程:{},打印请求头信息--》{}",Thread.currentThread(),age);
        }).start();
    }

结果:

注意:

阿里的ttl用法就是ThreadLocal的用法,具体参照:积累知识库:ThreadLocal在工作中是怎么使用_analysiscontext threadlocal-CSDN博客

相关推荐
MC皮蛋侠客几秒前
SQLAlchemy 系列(八):AsyncIO、并发与 Web 生命周期——让每个并发任务持有自己的 Session
数据库·python
alexwang2115 分钟前
HDU 4348 详细题解
c++·算法·题解·hdu·主席树·可持久化数据结构·可持久化线段树
前端 贾公子7 分钟前
第06章:结构化输出 (上)
java·服务器·前端
念何架构之路13 分钟前
restartmanager-重启管理子系统
java·开发语言
Java内核笔记27 分钟前
告别第三方库!Spring Boot 4 原生 API 版本控制全解析:4 种策略 + 实战案例
java·后端
可编程芯片开发1 小时前
基于分段变步长搜索RMDCFT算法的天基雷达空间机动目标检测方法MATLAB仿真,对比RMDCFT算法
算法
小小洋洋1 小时前
OpenWrt 从U盘迁移到内置 eMMC,并完成扩容与 Docker 安装
java·docker·eureka
2501_946736161 小时前
在线考试平台如何选型?从功能、优势与应用场景角度详解
大数据·人工智能·算法
就改了1 小时前
SpringBoot 自定义线程池 + 实时监控指标
java·spring boot·后端
天辛大师1 小时前
天心大师:不确定中锚定自我,AI生活的哲学命题
人工智能·算法·决策树·机器学习·生活·启发式算法