news 2026/7/22 14:01:35

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/7/22 6:00:45

Miniconda-Python3.10镜像中安装GeoPandas处理地理数据

Miniconda-Python3.10镜像中安装GeoPandas处理地理数据 在城市规划、环境监测和智慧交通等实际场景中&#xff0c;越来越多的数据科学家开始面对一个共同挑战&#xff1a;如何高效地处理带有地理位置信息的矢量数据&#xff1f;传统的GIS软件虽然功能强大&#xff0c;但操作繁琐…

作者头像 李华
网站建设 2026/7/22 0:01:59

i2s音频接口配置步骤:手把手带你完成初始化设置

手把手教你搞定 I2S 音频接口初始化&#xff1a;从原理到实战&#xff0c;零基础也能上手你有没有遇到过这样的情况&#xff1f;明明代码烧录成功、硬件连接也没问题&#xff0c;但音箱里传来的却是“滋滋”的噪音&#xff0c;或者左右声道颠倒、播放卡顿……一通排查下来&…

作者头像 李华
网站建设 2026/7/22 0:02:40

STM32多通道I2S音频传输核心要点

深入STM32多通道I2S音频系统&#xff1a;从时钟同步到DMA实战你有没有遇到过这样的问题——明明代码跑通了&#xff0c;音频也能播放&#xff0c;但总有些“咔哒”声、左右声道错乱&#xff0c;甚至长时间运行后声音开始跳帧&#xff1f;如果你正在用STM32做多路麦克风采集、工…

作者头像 李华
网站建设 2026/7/22 0:00:12

Jupyter Notebook在Miniconda-Python3.11中的启动与配置图文教程

Jupyter Notebook在Miniconda-Python3.11中的启动与配置图文教程 在高校实验室、AI创业团队或个人开发者的工作流中&#xff0c;你是否曾遇到过这样的场景&#xff1a;刚接手一个项目&#xff0c;却因为“环境不一致”导致代码跑不通&#xff1f;明明本地能运行的脚本&#xff…

作者头像 李华
网站建设 2026/7/21 5:12:26

Miniconda+PyTorch+GPU:构建高性能AI算力环境的技术路径

Miniconda PyTorch GPU&#xff1a;构建高性能AI算力环境的技术路径 在深度学习项目中&#xff0c;最让人头疼的往往不是模型设计本身&#xff0c;而是“为什么代码在我机器上跑得好好的&#xff0c;换台设备就报错&#xff1f;”——这种经典的“在我机器上能跑”问题&#…

作者头像 李华
网站建设 2026/7/14 9:27:08

Miniconda-Python3.10镜像中设置自动备份脚本的cron任务

在Miniconda-Python3.10镜像中配置基于cron的自动备份 在AI研究和数据科学项目中&#xff0c;一个常见的痛点是&#xff1a;辛辛苦苦训练了几天的模型、写了一周的代码&#xff0c;却因为一次误删或系统故障而全部丢失。更糟的是&#xff0c;很多开发者习惯于直接在Jupyter Not…

作者头像 李华