`
阅读更多
可能大部分人的想法和我当初的想法一样,都是以为在ThreadLocal里使用一个Map,这个Map的键为Thread,值为绑定的变量。其实如果这样做是有问题的:
1. 就是当线程回收时,该线程绑定的变量不能被自动的回收,因为变量存储在ThreadLocal里,必须显式的去回收。如果此变量存储在线程里,那么线程回收时,这个变量没有被其他引用指向的话,它便随着线程一起回收。
2. 另外不这样做还有一个好处:如果Map在ThreadLocal里,那你必须得考虑线程同步访问这个Map,但是这确实没有必要,因为线程访问自己的变量,和其他线程没有直接的关系,所以把Map放在线程里,就不需要做同步的处理,这样即加快了访问的速度。

其实实现不是这样的:每个线程都包含一个ThreadLocal.ThreadLocalMap变量threadLocals(延迟创建的),这个映射(Map)目的就是为每个线程存储关联到使用到的不同ThreadLocal的变量,这个很好理解,因为,一个线程可能使用到多个不同的ThreadLocal对象,每一个ThreadLocal对象的值都被认为是不同的。于是,每次调用ThreadLocal的get()方法,其实就是获取当前线程(Thread.currentThread()),然后从threadLocals映射里,根据ThreadLocal对象,找出其关联的拷贝,这个值便是当前线程的,隔离于其他线程的值。

我们知道,ThreadLocal.ThreadLocalMap映射使用的键是被WeakReference包装的ThreadLocal对象,如果ThreadLocal对象没有其他强引用和软引用指向时,该线程也不会继续持有ThreadLocal对象,因为根据JVM规范,它会被垃圾回收器下次回收时销毁,这一定程度避免了内存泄露,但不表示不会出现内存泄露,关于ThreadLocal引起的内存泄露,特别是导致ClassLoader不能被回收,网上有很多文章都在讨论。在Java 1.5开始,加入了remove()方法,这样我们可以显式的调用此方法,释放内存,所以使用ThreadLocal要特别注意内存泄露的问题。

看来大家对ThreadLocal内存泄露的原因有点误解,其导致Classloader内存泄露的原因在这篇博文写的很清楚了Classloader leaks:,我在这里补充一下,jvm规范下的classloader能被回收的条件是,所有该classloader产生的所有对象都被回收了:我们知道,对象有一个隐式的引用指向它的类型class对象,而class对象有个隐式的引用指向它的classloader,所以如果有一个对象不回收,那么可能导致整个classloader不能够被回收。根据sun的关于Perm回收的文章回应,perm在每次老生代(Tenured/Old generation)收集前回收。但是由于泄露,可能多次导致classloader不能被回收而引发Perm最终泄露。在Java 8里,开始不再使用Perm generation,会和JRockit等一样,使用Meta space放置class对象,这无疑是一个好消息。

特此申明一下,我的书籍http://redhat.iteye.com/admin/blogs/1007884关于4.2.3章节出现了纰漏,当我意识到错误时,花了1-2周来查看此问题,下载了sun jdk1.5版本,1.6版本,1.3版本,1.2版本,以及IBM的jdk代码进行比较,这个纰漏的申明在http://redhat.iteye.com/blog/1057991里,自己置顶,这个理解错误的原因是读取别人(老外)写的博文错误引起的,希望大家能够多读代码,谢谢!
分享到:
评论
12 楼 philip_kissme 2012-05-03  
如果使用线程池的话,继承jdk的线程池,重载afterExecute,回收你的threadlocal就可以了
建议在自己的项目里面对threadlocal封装一层,对唯一实例进行静态调用
protected void afterExecute(Runnable r, Throwable t) {
      ThreadContext.clear();
      logger.debug("clear thread context");
}
11 楼 redhat 2011-11-09  
这里再次说明下,Threadlocal在某些web服务器不能正常使用的原因是,那个web服务器会在一个httprequest使用完之后,重新使用处理这个request的线程做下一次处理,只是不会让两个request同时使用一个线程而已,怎样重用,取决于web服务器的具体实现。
10 楼 redhat 2011-05-27  
idle_sun 写道
LZ 写道
每次调用ThreadLocal的get()方法,其实就是获取当前线程(Thread.currentThread()),然后从threadLocals映射里,根据ThreadLocal对象,找出其关联的拷贝,这个值便是当前线程的,隔离于其他线程的值。"

这样的话,那其实我们调用ThreadLocal.set()存入的object实际是放在thread中的(thread.threadLocals这个映身射中),而这个map的key是TreadLocal对像,value是存入的object? 是这个意思吗..


是的
9 楼 idle_sun 2011-05-27  
LZ 写道
每次调用ThreadLocal的get()方法,其实就是获取当前线程(Thread.currentThread()),然后从threadLocals映射里,根据ThreadLocal对象,找出其关联的拷贝,这个值便是当前线程的,隔离于其他线程的值。"

这样的话,那其实我们调用ThreadLocal.set()存入的object实际是放在thread中的(thread.threadLocals这个映身射中),而这个map的key是TreadLocal对像,value是存入的object? 是这个意思吗..
8 楼 redhat 2011-05-26  
kingkan 写道
嗯,在使用线程池情况下,用完threadlocal一定要remove().

恩,看我最后新添加的部分。
7 楼 redhat 2011-05-26  
object_object 写道
学习了,看了以后还是少用ThreadLocal,自己管理

恩,看我最后新添加的部分。
6 楼 redhat 2011-05-26  
obullxl 写道
对的,特别是在使用线程池时,如果A任务执行完成后没有显示的remove()的话,当另外一个任务B也使用该线程时,就会把A的信息带到B中,会出现很多莫名其妙的问题。所以一般是try {} finally{ remove() }块来操作。

恩,看我最后新添加的部分。
5 楼 obullxl 2011-05-26  
对的,特别是在使用线程池时,如果A任务执行完成后没有显示的remove()的话,当另外一个任务B也使用该线程时,就会把A的信息带到B中,会出现很多莫名其妙的问题。所以一般是try {} finally{ remove() }块来操作。
4 楼 object_object 2011-05-26  
学习了,看了以后还是少用ThreadLocal,自己管理
3 楼 nature1 2011-05-25  
我也存在这样的误解,学习了
2 楼 kingkan 2011-05-25  
嗯,在使用线程池情况下,用完threadlocal一定要remove().
1 楼 wu_yong988 2011-05-25  
没错,
我遇到过一种情况,当使用threadlocal的时候由于线程不关闭,结果内存越加越多,造成了内存泄露,最后只好在完成某步骤后,手动remove。

相关推荐

    ThreadLocal的几种误区

    然而,ThreadLocal在理解和使用过程中容易产生一些误区,这里我们将详细探讨这些常见的误解。 误区一:ThreadLocal是Java线程的一个实现 ThreadLocal并非Java线程的实现,它只是一个工具类,用于创建线程局部变量。...

    ThreadLocal

    ThreadLocal是Java编程语言中...但在使用过程中,开发者需要注意内存管理和避免误解其为一种通用的线程同步机制,以防止潜在问题的发生。了解ThreadLocal的底层实现和工作原理对于优化并发代码和防止内存泄漏至关重要。

    TLS_TEST.rar_TLS_Thread Local Storage

    描述中提到的“线程局部存储”(TLS,也常被误解为Thread Local Storage)是编程语言中一个重要的特性,特别是在多线程环境下。它允许每个线程拥有自己独立的变量副本,这些副本只对创建它们的线程可见,避免了在...

    多核系统上的 Java 并发缺陷模式.doc

    `ThreadLocal`变量虽然在线程之间隔离,但如果线程池重用线程,之前的线程局部变量可能会影响到后续任务,导致意外的行为。要确保在每次任务开始时清理或重新设置线程局部变量。 6. **不恰当的同步对象选择**: ...

    2016奇虎360_JAVA研发工程师内推笔试题.zip_java interview_java 面试 雅虎 360

    标签“java_interview java_面试_雅虎_360”表明这个压缩包里的内容主要涉及Java语言的面试问题,但同样,雅虎(Yahoo)的提及可能是个误解。我们将重点关注360公司的Java面试要求。 从压缩包中唯一的文件“2016...

    java程序设计编码规范

    了解synchronized、volatile、ThreadLocal等机制,以及ExecutorService和并发工具类。 10. **测试**:编写单元测试和集成测试,确保代码正确性。JUnit是常用的Java测试框架,提倡TDD(测试驱动开发)。 以上只是...

    Spring.3.x企业应用开发实战(完整版).part2

    16.1.2 单元测试之误解 16.1.3 单元测试之困境 16.1.4 单元测试基本概念 16.2 JUnit 4快速进阶 16.2.1 JUnit 4概述 16.2.2 JUnit 4生命周期 16.2.3 使用JUnit 16.3 模拟利器Mockito 16.3.1 模拟测试概述 16.3.2 创建...

    Spring3.x企业应用开发实战(完整版) part1

    16.1.2 单元测试之误解 16.1.3 单元测试之困境 16.1.4 单元测试基本概念 16.2 JUnit 4快速进阶 16.2.1 JUnit 4概述 16.2.2 JUnit 4生命周期 16.2.3 使用JUnit 16.3 模拟利器Mockito 16.3.1 模拟测试概述 16.3.2 创建...

    JavaPuzzlers:JavaPuzzlers的学习

    5. **多线程与并发**:Java提供了丰富的并发工具,如synchronized、volatile、ThreadLocal等,但误用可能导致线程安全问题。理解并发原理并正确使用这些工具至关重要。 6. **异常处理**:Java的异常处理机制包括try...

Global site tag (gtag.js) - Google Analytics