渭南市网站建设_网站建设公司_需求分析_seo优化
2025/12/25 21:09:06 网站建设 项目流程

目录

  • 前言
  • 1. 基本知识
  • 2. 父子线程数据不同
  • 3. 父子线程数据相同

前言

Java基本知识:

  1. java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
  2. 【Java项目】实战CRUD的功能整理(持续更新)

1. 基本知识

本意大概如下:
基于 ThreadLocal 的请求级校验上下文容器(ValidatorDataContext,这是自定义的),用于在同一个请求线程内共享和传递校验/业务相关数据。它继承自ConcurrentHashMap<String, Object>,本质上是一个线程安全的 Key-Value 上下文,但通过 ThreadLocal 绑定到当前线程,从而保证每个请求线程拥有独立的数据副本,互不干扰

整体设计目的是:在校验(Validator)或业务处理链路中,作为一个与请求生命周期一致的上下文数据容器,方便不同校验器或组件在不显式传参的情况下共享数据,同时保证线程安全与请求隔离。

**线程池上下文(Thread Pool Context)**指的是:
在任务提交线程中已有的一些“隐式状态”,如何在被线程池复用的工作线程中继续可用

“隐式状态”通常包括:

登录用户信息(userId、token)
请求信息(requestId、traceId)
租户信息(tenantId)
语言/区域(Locale)
安全上下文(SecurityContext)
日志链路(MDC / Trace)
数据源路由信息
事务标记(⚠️)
线程池上下文的Demo如下:

1️⃣ ThreadLocal 的设计前提
ThreadLocal 的核心假设是:
一个请求 = 一个线程

但在线程池中:
线程是 复用的
一个线程会执行 多个不同请求的任务

➡️ 导致两个问题:

❌ 问题一:上下文丢失

ThreadLocal.set("userA");executor.submit(()->{// 这里读不到 userA});

原因:
任务在线程池的另一个线程执行


❌ 问题二:上下文污染(更危险)

ThreadLocal.set("userA");executor.submit(()->{// 执行完后没清理});executor.submit(()->{// 读到的是 userA(脏数据)});

原因:
线程池线程被复用,上一个任务的 ThreadLocal 没清



线程池的上下文Demo:

package线程池上下文;importlombok.Data;importjava.util.concurrent.ConcurrentHashMap;@DatapublicclassValidatorDataContextextendsConcurrentHashMap<String,Object>{/** * * 请求对象 */publicObjectrequestDto;publicObjectput(Stringkey,Objectvalue){if(key==null||value==null){returnnull;}else{returnsuper.put(key,value);}}/** * * ValidatorContext为请求上下文,与当前请求线程绑定,继承自ConcurrentHashMap */publicstaticfinalThreadLocal<?extendsValidatorDataContext>threadLocal=ThreadLocal.withInitial(()->newValidatorDataContext());/** * * 获取当前线程的上下文 * * @return */publicstaticValidatorDataContextgetCurrentContext(){ValidatorDataContextcontext=threadLocal.get();returncontext;}/** * * 设值 * * @param key * @param value */publicvoidset(Stringkey,Objectvalue){if(value!=null){put(key,value);}else{remove(key);}}/** * 设置值 * @param clazz 会自动取SimpleName为Key * @param value * @author K * 2020年10月12日下午6:38:01 */publicvoidsetByClass(Classclazz,Objectvalue){Stringkey=clazz.getSimpleName();set(key,value);}/** * * 获取String值 * * @param key * @return */publicStringgetString(Stringkey){return(String)get(key);}/**其他类型的值同理,进行强转换*//** * * 获取对象 * * @param <T> * @return */public<T>TgetByClazz(Class<T>clazz){if(get(clazz.getSimpleName())==null){returnnull;}else{return(T)get(clazz.getSimpleName());}}public<T>TgetRequestDto(Class<T>clazz){returnclazz.cast(requestDto);// 强制类型转换,更安全}publicvoidsetRequestDto(ObjectrequestDto){this.requestDto=requestDto;}}

2. 父子线程数据不同

子线程不会拷贝父线程的数据!

package线程池上下文;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.TimeUnit;// 模拟一个业务对象classUser{privateStringname;privateintage;publicUser(Stringname,intage){this.name=name;this.age=age;}publicStringgetName(){returnname;}publicintgetAge(){returnage;}}publicclassValidatorDataContextDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{// 创建线程池ExecutorServiceexecutor=Executors.newFixedThreadPool(2);// === 主线程:设置上下文 ===ValidatorDataContextcontext=ValidatorDataContext.getCurrentContext();context.set("traceId","TRACE-20251225-001");context.setRequestDto(newUser("Alice",30));context.setByClass(User.class,newUser("Bob",25));System.out.println("主线程 - traceId: "+context.getString("traceId"));System.out.println("主线程 - RequestDto name: "+context.getRequestDto(User.class).getName());System.out.println("主线程 - User class value name: "+context.getByClazz(User.class).getName());// === 在线程池中访问上下文 ===executor.submit(()->{// 注意:ThreadLocal 是线程隔离的,所以线程池线程无法直接获取主线程上下文ValidatorDataContextthreadContext=ValidatorDataContext.getCurrentContext();System.out.println("\n子线程1 - traceId: "+threadContext.getString("traceId"));// nullSystem.out.println("子线程1 - RequestDto: "+threadContext.getRequestDto());// null// 可以在子线程独立设置threadContext.set("traceId","THREAD-TRACE-001");System.out.println("子线程1 - 新 traceId: "+threadContext.getString("traceId"));});executor.submit(()->{ValidatorDataContextthreadContext=ValidatorDataContext.getCurrentContext();System.out.println("\n子线程2 - traceId: "+threadContext.getString("traceId"));// null});// 等待线程执行完executor.shutdown();executor.awaitTermination(5,TimeUnit.SECONDS);// === 主线程上下文依然可用 ===System.out.println("\n主线程 - traceId 再次访问: "+context.getString("traceId"));}}

截图如下:

3. 父子线程数据相同

在大的Demo下增加如下:

// 克隆当前上下文publicValidatorDataContextcopy(){ValidatorDataContextcopy=newValidatorDataContext();copy.putAll(this);copy.requestDto=this.requestDto;returncopy;}

总体的Demo测试如下:

package线程池上下文;importjava.util.concurrent.*;// 包装 Runnable,传递上下文classContextAwareRunnableimplementsRunnable{privatefinalRunnabletask;privatefinalValidatorDataContextcapturedContext;publicContextAwareRunnable(Runnabletask,ValidatorDataContextcontext){this.task=task;this.capturedContext=context.copy();// 拷贝上下文}@Overridepublicvoidrun(){ValidatorDataContextprevious=ValidatorDataContext.getCurrentContext();try{// 设置当前线程上下文为捕获的上下文ValidatorDataContext.threadLocal.set(capturedContext);task.run();}finally{// 执行完毕,恢复或清理上下文ValidatorDataContext.threadLocal.set(previous);}}}// 主类 DemopublicclassValidatorDataContextWithThreadPoolDemo{// 辅助方法:包装 RunnableprivatestaticRunnablewrap(Runnabletask){returnnewContextAwareRunnable(task,ValidatorDataContext.getCurrentContext());}publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{ExecutorServiceexecutor=Executors.newFixedThreadPool(2);// 主线程设置上下文ValidatorDataContextcontext=ValidatorDataContext.getCurrentContext();context.set("traceId","TRACE-20251225-001");context.setRequestDto(newUser("Alice",30));context.setByClass(User.class,newUser("Bob",25));System.out.println("主线程 - traceId: "+context.getString("traceId"));System.out.println("主线程 - RequestDto name: "+context.getRequestDto(User.class).getName());System.out.println("主线程 - User class value name: "+context.getByClazz(User.class).getName());// 提交线程池任务(自动继承主线程上下文)Future<?>future1=executor.submit(wrap(()->{ValidatorDataContextthreadContext=ValidatorDataContext.getCurrentContext();System.out.println("\n子线程1 - traceId: "+threadContext.getString("traceId"));System.out.println("子线程1 - RequestDto name: "+threadContext.getRequestDto(User.class).getName());System.out.println("子线程1 - User class value name: "+threadContext.getByClazz(User.class).getName());// 修改上下文不会影响主线程threadContext.set("traceId","THREAD-TRACE-001");}));Future<?>future2=executor.submit(wrap(()->{ValidatorDataContextthreadContext=ValidatorDataContext.getCurrentContext();System.out.println("\n子线程2 - traceId: "+threadContext.getString("traceId"));}));// 等待执行完成future1.get();future2.get();// 主线程上下文仍然保持原值System.out.println("\n主线程 - traceId 再次访问: "+context.getString("traceId"));executor.shutdown();executor.awaitTermination(5,TimeUnit.SECONDS);}}

截图如下:

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询