news 2026/9/2 16:32:34

Java ThreadLocal 内存泄漏代码示例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java ThreadLocal 内存泄漏代码示例

ThreadLocal不加static修饰时,会发生每个实例都有自己的 ThreadLocal 对象,这会导致严重的线程数据混乱问题。

本篇博文我通过如下代码来演示这个问题:

修改后的示例:不加static的 ThreadLocal

importjava.util.ArrayList;importjava.util.List;classSharedObject{privateintcounter=0;publicvoidincrement(){counter++;}publicintgetCounter(){returncounter;}}classThreadLocalNonStaticExample{// 注意:这里去掉了 static 修饰符!privateThreadLocal<SharedObject>threadLocal=ThreadLocal.withInitial(()->newSharedObject());publicvoidupdateCounter(){SharedObjectshared=threadLocal.get();shared.increment();System.out.println(Thread.currentThread().getName()+" - Counter: "+shared.getCounter()+" - ThreadLocal hash: "+System.identityHashCode(threadLocal));}publicstaticvoidmain(String[]args)throwsInterruptedException{// 创建两个不同的实例ThreadLocalNonStaticExampleexample1=newThreadLocalNonStaticExample();ThreadLocalNonStaticExampleexample2=newThreadLocalNonStaticExample();System.out.println("两个实例的 ThreadLocal 对象是否相同: "+(example1.threadLocal==example2.threadLocal));System.out.println("example1.threadLocal hash: "+System.identityHashCode(example1.threadLocal));System.out.println("example2.threadLocal hash: "+System.identityHashCode(example2.threadLocal));Threadthread1=newThread(()->{System.out.println("\n=== 线程1执行 ===");// 同一个线程,但访问不同实例的 ThreadLocalexample1.updateCounter();// 输出 1example1.updateCounter();// 输出 2example2.updateCounter();// 输出 1 - 因为是不同的 ThreadLocal!},"Thread-1");Threadthread2=newThread(()->{System.out.println("\n=== 线程2执行 ===");example1.updateCounter();// 输出 1 - 新线程,新的 ThreadLocalMapexample2.updateCounter();// 输出 1},"Thread-2");thread1.start();thread1.join();thread2.start();thread2.join();}}

更复杂的场景:内存泄漏风险

classMemoryLeakExample{// 非 static 的 ThreadLocalprivateThreadLocal<List<String>>dataThreadLocal=ThreadLocal.withInitial(()->newArrayList<>());publicvoidaddData(Stringdata){dataThreadLocal.get().add(data);}publicvoidprintData(){System.out.println(Thread.currentThread().getName()+" 数据: "+dataThreadLocal.get());}publicstaticvoidmain(String[]args)throwsInterruptedException{// 模拟大量创建实例List<MemoryLeakExample>examples=newArrayList<>();Threadthread=newThread(()->{for(inti=0;i<1000;i++){MemoryLeakExampleexample=newMemoryLeakExample();examples.add(example);example.addData("data-"+i);if(i%100==0){System.out.println("创建了 "+i+" 个实例");System.out.println("当前线程 ThreadLocalMap 中的 Entry 数量会不断增加...");}}// 每个实例都有自己的 ThreadLocal 对象作为 key// 在 ThreadLocalMap 中会有大量 Entry!});thread.start();thread.join();System.out.println("\n总计创建了 "+examples.size()+" 个实例");System.out.println("这意味着在当前线程的 ThreadLocalMap 中有 "+examples.size()+" 个不同的 ThreadLocal key!");}}

在线程池中的灾难性后果

importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;classThreadPoolProblem{// 非 static ThreadLocalprivateThreadLocal<Integer>counterThreadLocal=ThreadLocal.withInitial(()->0);publicvoidincrement(){Integercount=counterThreadLocal.get();counterThreadLocal.set(count+1);}publicintgetCount(){returncounterThreadLocal.get();}publicstaticvoidmain(String[]args)throwsInterruptedException{ExecutorServiceexecutor=Executors.newFixedThreadPool(2);// 任务:每个任务创建一个新实例for(inti=0;i<10;i++){inttaskId=i;executor.submit(()->{ThreadPoolProbleminstance=newThreadPoolProblem();instance.increment();instance.increment();System.out.println("任务 "+taskId+",线程: "+Thread.currentThread().getName()+",计数: "+instance.getCount()+",ThreadLocal hash: "+System.identityHashCode(instance.counterThreadLocal));// 问题:线程池线程复用,但每次任务都创建新的 ThreadLocal// 导致 ThreadLocalMap 中积累大量 stale entries});}executor.shutdown();}}

不加static的问题总结

  1. 线程隔离失效

    • 每个实例有自己的ThreadLocal对象
    • 同一个线程访问不同实例时,会使用不同的ThreadLocalkey
    • 无法实现真正意义上的线程级数据共享
  2. 内存泄漏

    // 每次创建新实例ThreadLocalNonStaticExampleobj=newThreadLocalNonStaticExample();// 都会创建新的 ThreadLocal 对象// ThreadLocalMap 中会积累:ThreadLocal(key) -> value// 线程不结束,这些 entry 就一直存在
  3. ThreadLocalMap 膨胀

    • 每个线程的ThreadLocalMap中会有大量 key(每个实例一个)
    • 即使实例被回收,ThreadLocal 是弱引用,但 value 可能还在
  4. 数据不一致

    // 线程1example1.updateCounter();// 使用 example1.threadLocalexample2.updateCounter();// 使用 example2.threadLocal(不同的存储位置!)// 它们不是同一个 ThreadLocal,所以不共享数据
  5. 性能问题

    • 每次都要创建新的ThreadLocal对象
    • ThreadLocalMap查找效率降低(hash 冲突增加)

正确 vs 错误对比

// ✅ 正确:static ThreadLocalpublicclassCorrectExample{privatestaticThreadLocal<Context>context=ThreadLocal.withInitial(Context::new);// 所有实例共享同一个 ThreadLocal 对象// 线程数据真正隔离}// ❌ 错误:非 static ThreadLocalpublicclassWrongExample{privateThreadLocal<Context>context=ThreadLocal.withInitial(Context::new);// 每个实例都有自己的 ThreadLocal// 线程数据混乱}

结论ThreadLocal必须使用static修饰,确保所有实例共享同一个ThreadLocal对象作为 key,这样才能正确实现线程级别的数据隔离。

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

Linux显卡信息查询与排错

查看显卡情况 lspci | grep -i vga lspci | grep -E "VGA|3D|Display" 显示 04:00.0 VGA compatible controller: ASPEED Technology, Inc. ASPEED Graphics Family (rev 41) 4b:00.0 VGA compatible controller: NVIDIA Corporation Device 2204 (rev a1) 65:00.0 …

作者头像 李华
网站建设 2026/8/27 17:29:30

Python高级技巧:利用Miniconda-Python3.10实现多项目环境隔离

Python高级技巧&#xff1a;利用Miniconda-Python3.10实现多项目环境隔离 在人工智能与数据科学的开发实践中&#xff0c;你是否曾遇到过这样的场景&#xff1f;一个刚跑通的模型&#xff0c;在换了一台机器后却因“找不到模块”或“版本不兼容”而彻底罢工&#xff1b;又或者&…

作者头像 李华
网站建设 2026/8/27 17:29:30

vue基于django教室预约管理系统

目录已开发项目效果实现截图关于博主开发技术路线相关技术介绍核心代码参考示例结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;已开发项目效果实现截图 同行可拿货,招校园代理 ,本人源头供货商 vue基于django教室预约管理系统 …

作者头像 李华
网站建设 2026/8/27 17:29:52

使用Miniconda创建独立环境避免PyTorch与TensorFlow版本冲突

使用Miniconda创建独立环境避免PyTorch与TensorFlow版本冲突 在现代AI开发中&#xff0c;一个令人头疼的现实是&#xff1a;你刚跑通的项目&#xff0c;在同事机器上却“无法导入模块”&#xff1b;或者你想复现一篇论文代码&#xff0c;却发现它依赖的是早已被弃用的框架旧版本…

作者头像 李华
网站建设 2026/8/27 15:58:42

如何在Miniconda环境中同时安装PyTorch和Transformers库

如何在 Miniconda 环境中同时安装 PyTorch 和 Transformers 库 在当今的 AI 开发实践中&#xff0c;搭建一个稳定、可复现且高效的深度学习环境&#xff0c;几乎是每个项目的第一步。尤其是在处理自然语言任务时&#xff0c;PyTorch Transformers 已成为事实上的标准组合。然…

作者头像 李华
网站建设 2026/8/27 16:00:14

使用flit发布Python包到Miniconda环境

使用 Flit 发布 Python 包到 Miniconda 环境 在数据科学、AI 工程和自动化工具开发的日常中&#xff0c;一个常见的挑战是&#xff1a;如何快速、干净地将一个小而精的工具库发布为可复用的 Python 包&#xff0c;并确保它能在团队成员或 CI/CD 流水线中无缝安装&#xff1f;尤…

作者头像 李华