ThreadLocal研究

我在hibernate工厂类中看到了关于ThreadLocal的使用,threadLocal显然是类变量,全局的
是否这样表示不同的线程共享了threadLocal,事实上没错,多个线程取得到是同一样threadLocal
但是在不同的线程中使用threadLocal得到的变量不一样。那么什么是同一线程呢,我们可以这样理解
线程是主线程创建分支线程调用类执行完再返回主线程,那么这么这分支线程上所执行的所有对象的
方法实际上是同一个线程,除非中间使用多线程方法去创建其它分支线程。因此从创建分支线程
开始的那个方法一直执行到返回都是在同一线程中,其中引用的类,对象,方法都属性同一线程。

显示当前线程的方法为:
System.out.println(Thread.currentThread());

那么线程和对象方法有什么关系呢。当创建线程分支时,很显然就决定一个程序执行的线,
这个线的入口就是创建线程时给的方法的入口,方法一直向下执行,一部部的调用引用的其它类中的方法
如果碰到了实例化就加载类,如果类已加载过就直接实例化,如果static,在加载时就实际上分配了地址
直接跳到引用的地址继续执行,一直到执行完后返最后的代码。

在测试的过程中发现我们请求不同的页面时调用threadLocal.get()的结果一样的,这就有点奇怪了,
我测试的服务器是tomcat6.0+JDK1.6,为了模拟并发,我在其中一个线程中使用Thread.sleep(10000)
让线程等待10秒钟,结果发现再去请求另一个页面执行的线程并不一样,但是发现后面一个请求用的是前面
用过的一个线程,因此从这个线程得到的get()和前面执行完的那个线程一样的,只是和等待的那个线
程不一样。

因此有如下结论:
1)web请求的时分配的线程可能是以前用过的一个旧线程,线程保存着原来的一些值。
2)当并发产生时,会分配新的线程,新的线程和同时发生的别一个线程不是同一个线程,但是都有可能是以前的旧线程。
3)tomcat用的是线程池,有点类似于数据的连接池。
4)因此我们用完一个threadLocal应对其进行清除,以免下一个请求得到旧数据而产生影响。


在研究测试中发现。
1)Thread中有一个属性为:ThreadLocal.ThreadLocalMap threadLocals = null;
也就是每一个线程都有这么一个ThreadLocalMap,这是线程所有固有的。当然如果没有使用到,这只是一个空属性。

2)而ThreadLoacl中的方法创建了一个ThreadLocal.ThreadLocalMap实例,相当于
threadLocals = new ThreadLocalMap();

3)因此每一个线程都有一个ThreadLocalMap,只是没有使用时是空的罢了。

public T get()
{
Thread localThread = Thread.currentThread();
ThreadLocalMap localThreadLocalMap = getMap(localThread);
if (localThreadLocalMap != null)
return ThreadLocalMap.access$000(localThreadLocalMap, this);
Object localObject = initialValue();
createMap(localThread, localObject);
return localObject;
}

public void set(T paramT)
{
Thread localThread = Thread.currentThread();
ThreadLocalMap localThreadLocalMap = getMap(localThread);
if (localThreadLocalMap != null)
ThreadLocalMap.access$100(localThreadLocalMap, this, paramT);
else
createMap(localThread, paramT);
}

ThreadLocalMap getMap(Thread paramThread)
{
return paramThread.threadLocals;
}
4)从上面ThreadLoacl原码可知getMap实际上就是得到指定线程的threadLocals值,当然threadLocals可能为null,而threadLocals就是一个ThreadLocal.ThreadLocalMap
5)我们看到上面的set方法也是一样的,调用了getMap方法。
6)Thread中的threadLocals 虽然没有说明作用域,但测试发现是protected类型,只有在子类或者同一个包中才能引用到。
因此我们只能通过和Thread的同一个包中的类来引用它。


------------------------------------------------------------------------------------
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
/**
* 得到session,此sessiond存有全属对象.
* @return //Session
*/
public static Session getSession()
{
Session re = threadLocal.get(); //这个地方不同的线程得到的值不一样的。
if(re==null) //此方法看线程变量中是否有值,如果有就用现存的值,没有就新实例化一个。
{
re = new Session();
threadLocal.set(re);
}
return re;
}

--------------------------------------------------------------
测试:1)看是否protected在同一个包中是否可引用。
测试结果:可以引用。

总结:private 只有在类内访问,就算是内部类也不能访问。
protected 类内,子类,同一个包中的类可以访问。
public 只要指明在那个包中,任何其它类都可能访问。
### ThreadLocal 导致 OOM 的原因 ThreadLocal 是一种用于实现线程局部变量的机制,在多线程环境下,每个线程都可以拥有自己的独立副本。然而,如果使用不当,可能会引发内存泄漏问题,最终导致 `java.lang.OutOfMemoryError: Java heap space` 错误。 #### 原因分析 1. **ThreadLocal 的内部结构** ThreadLocal 使用一个名为 `ThreadLocalMap` 的哈希表来存储数据[^2]。这个 Map 的键是 ThreadLocal 对象本身,而值则是该线程对应的本地变量。当 Thread 结束时,如果没有显式清理这些资源,则可能导致 ThreadLocalMap 中的数据无法被回收。 2. **弱引用与强引用的关系** 在 ThreadLocalMap 中,键(即 ThreadLocal 实例)是以弱引用的方式保存的,而值则以强引用方式保存。这意味着即使 ThreadLocal 被设置为 null 并从外部不可达,只要其关联的线程仍然存活,那么它的值就不会被垃圾回收器释放[^3]。 3. **Tomcat 场景下的特殊行为** 在 Tomcat 或其他类似的 Web 容器中,可能存在多个请求共享同一个工作线程的情况。一旦某个请求创建了一个未正确清除的 ThreadLocal 变量,后续请求可能继承此变量并持续占用内存。更糟糕的是,如果异常发生使得线程调度出现问题,ThreadLocal 所持有的对象将长期驻留在堆上,直到触发 Full GC 甚至 OOM。 --- ### 解决方案 针对由 ThreadLocal 引发的 OutOfMemory 问题,可以采取以下措施: #### 方法一:及时清空 ThreadLocal 数据 在每次使用完 ThreadLocal 后,应立即调用 `remove()` 方法将其对应条目从当前线程的地图中移除。这一步骤非常重要,因为只有这样才能够确保废弃的对象能够进入可回收状态。 ```java public void someMethod() { try { MyThreadLocal.set(new LargeObject()); // Do something with the large object... } finally { MyThreadLocal.remove(); // Ensure cleanup happens here. } } ``` #### 方法二:合理设计 ThreadLocal 生命周期 尽量减少全局范围内的静态 ThreadLocal 定义数量,转而在必要范围内定义它们。例如,仅限于特定类或者方法的作用域内声明临时性的实例化版本。这样做有助于缩小潜在风险的影响面。 #### 方法三:监控与诊断工具的应用 利用 JVM 自带命令行选项以及第三方性能剖析软件定期检查应用程序是否存在不必要的内存增长现象。比如通过 `-XX:+HeapDumpOnOutOfMemoryError` 参数生成崩溃时刻完整的 dump 文件以便事后深入研究根本成因;也可以借助 VisualVM、JProfiler 等图形界面型调试辅助手段动态观察实时变化趋势图谱。 #### 方法四:调整 JVM 参数优化堆大小配置 适当增大初始分配空间(-Xms) 和最大允许容量 (-Xmx),给予程序更多喘息余地应对突发状况下瞬时激增的需求压力。不过需要注意平衡硬件资源配置以免过度消耗物理 RAM 影响整体效率表现[^1]: ```bash java -Xms512m -Xmx2g YourApplicationMainClass ``` --- ### 总结 综上所述,虽然 ThreadLocal 提供了一种简单易用的方式来隔离各线程间的状态信息传递过程,但如果忽视了对其生命周期的有效管理,则很容易陷入严重的资源浪费困境之中——尤其是在线程池频繁复用场景下尤为突出。因此建议开发者们养成良好习惯,在每一次操作完毕之后都记得主动销毁无用连接关系链路节点以防万一。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值