`
阅读更多
可能大部分人的想法和我当初的想法一样,都是以为在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)是Java编程中一个非常重要的工具类,它在多线程环境下提供了线程安全的数据存储。ThreadLocal并不是一个变量,而是一个类,它为每个线程都创建了一个独立的变量副本,使得每个线程都...

    ThreadLocal

    ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...

    ThreadLocal的几种误区

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

    ThreadLocal 内存泄露的实例分析1

    在 `LeakingServlet` 的 `doGet` 方法中,如果 `ThreadLocal` 没有设置值,那么会创建一个新的 `MyCounter` 并设置到 `ThreadLocal` 中。关键在于,一旦 `MyCounter` 被设置到 `ThreadLocal`,那么它将与当前线程...

    正确理解ThreadLocal.pdf

    ### 正确理解ThreadLocal:深入解析其工作原理与应用场景 #### 一、ThreadLocal的基本概念 `ThreadLocal`是Java平台提供的一种线程局部变量的解决方案,它为每一个使用该变量的线程都提供了独立的变量副本,使得每...

    理解ThreadLocal

    理解ThreadLocal 理解ThreadLocal 理解ThreadLocal 理解ThreadLocal

    java 简单的ThreadLocal示例

    Java中的ThreadLocal是一个非常重要的工具类,它在多线程编程中扮演着独特角色,尤其在处理线程间数据隔离和共享时。ThreadLocal不是线程本身,而是为每个线程提供一个独立的变量副本,使得每个线程都可以独立地改变...

    ThreadLocal整理.docx

    ThreadLocal 整理 ThreadLocal 是 Java 中的一个重要组件,它能够在每个线程中保持独立的副本。这个功能是通过 Thread 类中的 threadLocals 属性来实现的,这个属性实际上是一个 Entry 数组,其中的每个 Entry 都...

    java事务 - threadlocal

    Java事务和ThreadLocal是两种在Java编程中至关重要的概念,它们分别用于处理多线程环境下的数据一致性问题和提供线程局部变量。 首先,我们来深入理解Java事务。在数据库操作中,事务是一系列操作的集合,这些操作...

    java中ThreadLocal详解

    ### Java中ThreadLocal详解 #### 一、ThreadLocal概述 在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,...

    ThreadLocal简单Demo

    **线程局部变量(ThreadLocal)** 在Java编程中,`ThreadLocal`是一个非常重要的工具类,它用于在多线程环境中提供线程安全的局部变量。`ThreadLocal`并不是一个线程,而是一个线程局部变量的容器,每个线程都有自己...

    ThreadLocal原理及在多层架构中的应用

    **线程局部变量(ThreadLocal)是Java编程中一个非常重要的概念,主要用于在多线程环境中为每个线程提供独立的变量副本。ThreadLocal不是一种数据结构,而是一种解决线程间共享数据的方式,它提供了线程安全的局部...

    设计模式及ThreadLocal资料

    本资料主要聚焦于两种设计模式以及Java中的ThreadLocal特性。 首先,我们来探讨单例模式。单例模式是一种确保一个类只有一个实例,并提供全局访问点的设计模式。在Java中,通常通过私有构造函数、静态工厂方法或...

    ThreadLocal_ThreadLocal源码分析_

    **ThreadLocal概述** ThreadLocal是Java中的一个线程局部变量类,它为每个线程创建了一个独立的变量副本。这意味着每个线程都有自己的ThreadLocal变量,互不干扰,提供了线程安全的数据存储方式。ThreadLocal通常...

    使用ThreadLocal管理“session”数据

    在Java编程中,ThreadLocal是线程局部变量的类,它提供了一种在多线程环境中为每个线程创建和维护独立副本的机制。ThreadLocal主要用于解决线程间的数据隔离问题,确保各线程拥有自己的变量副本,避免了数据共享带来...

    threadLocal

    ThreadLocal是Java编程语言中的一个线程局部变量类,它为每个线程提供了一个独立的变量副本,使得每个线程可以独立地改变自己的副本,而不会影响其他线程所对应的副本。这个特性在多线程环境下处理并发问题时非常...

    ThreadLocal详解.md

    学习ThreadLocal,了解其中的原理,以及学习其中的优点!避免坑点!!

Global site tag (gtag.js) - Google Analytics