news 2026/2/4 15:32:49

Java 代码示例:ThreadLocal 解决同一个线程内多个对象间共享状态更新

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 代码示例:ThreadLocal 解决同一个线程内多个对象间共享状态更新

示例1:普通共享对象的问题

importjava.util.ArrayList;importjava.util.List;classSharedObject{privateintcounter=0;publicvoidincrement(){counter++;}publicintgetCounter(){returncounter;}}classThreadLocalNotWorkingExample{privatestaticThreadLocal<SharedObject>threadLocal=ThreadLocal.withInitial(()->newSharedObject());publicvoidupdateCounter(){SharedObjectshared=threadLocal.get();shared.increment();System.out.println(Thread.currentThread().getName()+" - Counter: "+shared.getCounter());}publicstaticvoidmain(String[]args)throwsInterruptedException{ThreadLocalNotWorkingExampleexample1=newThreadLocalNotWorkingExample();ThreadLocalNotWorkingExampleexample2=newThreadLocalNotWorkingExample();// 同一个线程内,不同实例共享同一个 SharedObjectThreadthread=newThread(()->{// 两个不同对象的方法调用example1.updateCounter();// 输出 1example2.updateCounter();// 输出 2 - 问题:共享了同一个 SharedObject 实例},"Thread-1");thread.start();thread.join();}}

示例2:多个对象间共享状态导致的竞态条件

importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;classOrderService{privatestaticThreadLocal<List<String>>orderThreadLocal=ThreadLocal.withInitial(ArrayList::new);publicvoidaddOrder(StringorderId){List<String>orders=orderThreadLocal.get();orders.add(orderId);System.out.println(Thread.currentThread().getName()+" 添加订单: "+orderId+",当前订单数: "+orders.size());}publicvoidprocessOrders(){List<String>orders=orderThreadLocal.get();// 模拟处理订单for(Stringorder:orders){System.out.println(Thread.currentThread().getName()+" 处理订单: "+order);}}}classPaymentService{privatestaticThreadLocal<List<String>>paymentThreadLocal=ThreadLocal.withInitial(ArrayList::new);publicvoidaddPayment(StringpaymentId){List<String>payments=paymentThreadLocal.get();payments.add(paymentId);System.out.println(Thread.currentThread().getName()+" 添加支付: "+paymentId+",当前支付数: "+payments.size());}}classProblemExample{privatestaticThreadLocal<SharedState>threadLocal=ThreadLocal.withInitial(SharedState::new);staticclassSharedState{List<String>allOperations=newArrayList<>();intoperationCount=0;publicsynchronizedvoidaddOperation(Stringoperation){allOperations.add(operation);operationCount++;}}publicvoiddoOperation1(){SharedStatestate=threadLocal.get();state.addOperation("Operation1");System.out.println("Operation count: "+state.operationCount);}publicvoiddoOperation2(){SharedStatestate=threadLocal.get();state.addOperation("Operation2");System.out.println("Operation count: "+state.operationCount);}publicstaticvoidmain(String[]args){ProblemExampleobj1=newProblemExample();ProblemExampleobj2=newProblemExample();// 使用线程池,线程会被复用ExecutorServiceexecutor=Executors.newFixedThreadPool(2);for(inti=0;i<5;i++){inttaskId=i;executor.submit(()->{System.out.println("\n=== 任务 "+taskId+",线程: "+Thread.currentThread().getName()+" ===");// 同一个线程内,不同对象共享同一个 ThreadLocal 的 SharedStateobj1.doOperation1();obj2.doOperation2();// 这里会操作同一个 SharedState// 问题:线程复用时,ThreadLocal 里的数据没有清理SharedStatestate=threadLocal.get();System.out.println("总共操作数: "+state.operationCount+",操作列表: "+state.allOperations);});}executor.shutdown();}}

示例3:解决方案 - 显式传递共享状态

importjava.util.concurrent.ConcurrentHashMap;// 方案1:使用显式参数传递classOrderContext{privatefinalStringthreadId;privatefinalList<String>orders=newArrayList<>();privatefinalList<String>payments=newArrayList<>();publicOrderContext(StringthreadId){this.threadId=threadId;}publicsynchronizedvoidaddOrder(Stringorder){orders.add(order);}publicsynchronizedvoidaddPayment(Stringpayment){payments.add(payment);}publicvoidprocess(){System.out.println("处理线程 "+threadId+" 的订单和支付");System.out.println("订单: "+orders);System.out.println("支付: "+payments);}}classOrderProcessor{// 显式传递上下文,而不是使用 ThreadLocalpublicvoidprocessOrder(OrderContextcontext,StringorderId){context.addOrder(orderId);}}classPaymentProcessor{publicvoidprocessPayment(OrderContextcontext,StringpaymentId){context.addPayment(paymentId);}}// 方案2:使用请求级别的上下文classRequestContext{privatestaticfinalThreadLocal<RequestContext>contextThreadLocal=newThreadLocal<>();privatefinalStringrequestId;privatefinalMap<String,Object>attributes=newConcurrentHashMap<>();privateRequestContext(StringrequestId){this.requestId=requestId;}publicstaticvoidcreateContext(StringrequestId){contextThreadLocal.set(newRequestContext(requestId));}publicstaticRequestContextgetCurrentContext(){returncontextThreadLocal.get();}publicstaticvoidclearContext(){contextThreadLocal.remove();}publicvoidsetAttribute(Stringkey,Objectvalue){attributes.put(key,value);}publicObjectgetAttribute(Stringkey){returnattributes.get(key);}publicStringgetRequestId(){returnrequestId;}}classSolutionExample{publicstaticvoidmain(String[]args){// 模拟HTTP请求处理for(inti=0;i<3;i++){StringrequestId="REQ-"+i;// 开始请求,创建上下文RequestContext.createContext(requestId);try{// 处理请求RequestContextcontext=RequestContext.getCurrentContext();// 不同服务可以共享这个上下文OrderService2orderService=newOrderService2();PaymentService2paymentService=newPaymentService2();orderService.createOrder("ORDER-123");paymentService.processPayment("PAY-456");// 处理业务逻辑processBusinessLogic();}finally{// 必须清理,防止内存泄漏RequestContext.clearContext();}}}staticvoidprocessBusinessLogic(){RequestContextcontext=RequestContext.getCurrentContext();System.out.println("处理请求 "+context.getRequestId()+" 的业务逻辑");}}classOrderService2{publicvoidcreateOrder(StringorderId){RequestContextcontext=RequestContext.getCurrentContext();context.setAttribute("orderId",orderId);System.out.println("创建订单: "+orderId+",请求ID: "+context.getRequestId());}}classPaymentService2{publicvoidprocessPayment(StringpaymentId){RequestContextcontext=RequestContext.getCurrentContext();StringorderId=(String)context.getAttribute("orderId");System.out.println("处理支付: "+paymentId+",对应订单: "+orderId);}}

关键总结

  1. ThreadLocal 的共享问题

    • 同一个线程内的不同对象通过static ThreadLocal访问的是同一个对象实例
    • 这可能导致线程内多个组件间意外的状态共享
  2. 解决方案

    • 方案1:显式传递上下文对象作为方法参数
    • 方案2:使用请求/会话级别的上下文,并在处理完成后及时清理
    • 方案3:如果确实需要线程内共享,使用专门设计的共享对象,并确保线程安全
  3. 最佳实践

    // 正确使用 ThreadLocal 的模式try{threadLocal.set(value);// 执行业务逻辑}finally{threadLocal.remove();// 必须清理}

核心原则ThreadLocal适用于存储线程隔离的上下文信息,而不是用于同一个线程内不同对象间共享和更新状态。如果需要线程内对象间共享状态,应考虑其他设计模式。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/1 11:50:57

Miniconda-Python3.10镜像中使用iostat监控磁盘IO

Miniconda-Python3.10镜像中使用iostat监控磁盘IO 在AI模型训练过程中&#xff0c;你是否遇到过这样的情况&#xff1a;GPU利用率长期徘徊在20%以下&#xff0c;而CPU却忙得不可开交&#xff1f;看起来代码跑起来了&#xff0c;但整个训练任务像蜗牛一样缓慢。这种“高资源投入…

作者头像 李华
网站建设 2026/1/29 19:48:10

Miniconda-Python3.10镜像中配置SSH免密登录跳板机

Miniconda-Python3.10 镜像中配置 SSH 免密登录跳板机 在现代 AI 工程实践中&#xff0c;一个常见的痛点是&#xff1a;你已经写好了训练脚本、环境也配好了&#xff0c;却卡在“怎么安全又高效地连上远程 GPU 节点”这件事上。每次输入密码不仅繁琐&#xff0c;还让自动化成了…

作者头像 李华
网站建设 2026/1/29 19:49:41

在云服务器上部署Miniconda-Python3.11并运行PyTorch训练任务

在云服务器上部署 Miniconda-Python3.11 并运行 PyTorch 训练任务 在当今 AI 研发节奏日益加快的背景下&#xff0c;一个常见却令人头疼的问题浮出水面&#xff1a;为什么代码在本地能跑&#xff0c;在服务器上却报错&#xff1f;依赖版本不一致、Python 环境混乱、GPU 驱动不匹…

作者头像 李华
网站建设 2026/1/29 18:30:29

Miniconda-Python3.10镜像中设置ulimit提升文件句柄数

Miniconda-Python3.10镜像中设置ulimit提升文件句柄数 在构建大规模AI训练环境或运行高并发数据处理任务时&#xff0c;你是否曾遇到过这样的报错&#xff1f; OSError: [Errno 24] Too many open files这行看似简单的错误&#xff0c;往往出现在最不该出现的时刻——模型已经跑…

作者头像 李华
网站建设 2026/1/29 10:57:08

Miniconda-Python3.10镜像配合GitHub Actions实现CI/CD流水线

Miniconda-Python3.10镜像配合GitHub Actions实现CI/CD流水线 在数据科学与AI开发的日常中&#xff0c;你是否曾遇到这样的场景&#xff1a;本地训练模型一切正常&#xff0c;推送到仓库后CI却报错“找不到模块”&#xff1f;或者团队成员反复追问“你的环境是怎么装的&#xf…

作者头像 李华