我对ThreadLocal的一些理解 内存泄露啥的
1. 什么是ThreadLocal
ThreadLocal提供了线程的局部变量,每个线程都可以通过set()和get()来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离~。
简要言之:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。
ThreadLocal用在什么地方?
讨论ThreadLocal用在什么地方前,我们先明确下,如果仅仅就一个线程,那么都不用谈ThreadLocal的,ThreadLocal是用在多线程的场景的!!!
ThreadLocal归纳下来就2类用途:
保存线程上下文信息,在任意需要的地方可以获取!!!
线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失!!!
保存线程上下文信息,在任意需要的地方可以获取!!!
由于ThreadLocal的特性,同一线程在某地方进行设置,在随后的任意地方都可以获取到。从而可以用来保存线程上下文信息。
常用的比如每个请求怎么把一串后续关联起来,就可以用ThreadLocal进行set,在后续的任意需要记录日志的方法里面进行get获取到请求id,从而把整个请求串起来。
还有比如Spring的事务管理,用ThreadLocal存储Connection,ThreadLocal能够实现当前线程的操作都是用同一个Connection,保证了事务!从而各个DAO可以获取同一Connection,可以进行事务回滚,提交等操作。
同样的,Hibernate对Connection的管理也是采用了相同的手法(使用ThreadLocal,当然了Hibernate的实现是更强大的)~
线程安全的,避免某些情况需要考虑线程安全必须同步带来的性能损失!!!
每个线程往ThreadLocal中读写数据是线程隔离,互相之间不会影响的,所以ThreadLocal无法解决共享对象的更新问题!
由于不需要共享信息,自然就不存在竞争问题了,从而保证了某些情况下线程的安全,以及避免了某些情况需要考虑线程安全必须同步带来的性能损失!!!
这类场景阿里规范里面也提到了:
ThreadLocal一些细节!
ThreaLocal使用示例代码:
public class ThreadLocalTest {
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
threadLocal.set(i);
System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
threadLocal.remove();
}
}, "threadLocal1").start();
new Thread(() -> {
try {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "====" + threadLocal.get());
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} finally {
threadLocal.remove();
}
}, "threadLocal2").start();
}
}
代码运行结果:
从运行的结果我们可以看到threadLocal1进行set值对threadLocal2并没有任何影响!
Thread、ThreadLocalMap、ThreadLocal总览图:
Thread类有属性变量threadLocals (类型是ThreadLocal.ThreadLocalMap),也就是说每个线程有一个自己的ThreadLocalMap ,所以每个线程往这个ThreadLocal中读写隔离的,并且是互相不会影响的。
一个ThreadLocal只能存储一个Object对象,如果需要存储多个Object对象那么就需要多个ThreadLocal!!!
如图:
看到上面的几个图,大概思路应该都清晰了,我们Entry的key指向ThreadLocal用虚线表示弱引用 。
2. ThreadLocal实现的原理
首先,我们来看一下ThreadLocal的set()方法,因为我们一般使用都是new完对象,就往里边set对象了
public void set(T value)
{
// 得到当前线程对象
Thread t = Thread.currentThread();
// 这里获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
上面有个ThreadLocalMap,我们去看看这是什么?
static class ThreadLocalMap
{
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//....很长
}
通过上面我们可以发现的是ThreadLocalMap是ThreadLocal的一个内部类。用Entry类来进行存储
我们的值都是存储到这个Map上的,key是当前ThreadLocal对象!
如果该Map不存在,则初始化一个。如果该Map存在,则从Thread中获取!
Thread维护了ThreadLocalMap变量 。
可以看出,ThreadLocalMap是在ThreadLocal中使用内部类来编写的,但对象的引用是在Thread中!
于是我们可以总结出:Thread为每个线程维护了ThreadLocalMap这么一个Map,而ThreadLocalMap的key是LocalThread对象本身,value则是要存储的对象。
有了上面的基础,我们看get()方法就一点都不难理解了:
public T get()
{
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocal原理总结
(1)每个Thread维护着一个ThreadLocalMap的引用
(2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
(3)调用ThreadLocal的set()方法时,实际上就是往ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
(4)调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
(5)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。
正因为这个原理,所以ThreadLocal能够实现“数据隔离”,获取当前线程的局部变量值,不受其他线程影响~
3. 避免内存泄露
我们来看一下ThreadLocal的对象关系引用图:
我们Entry的key指向ThreadLocal用虚线表示弱引用。
java对象的引用包括 :强引用,软引用,弱引用,虚引用 。
因为这里涉及到弱引用,简单说明下:
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,该对象仅仅被弱引用关联,那么就会被回收。
当仅仅只有ThreadLocalMap中的Entry的key指向ThreadLocal的时候,ThreadLocal会进行回收的!!!
ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,但是Entry是强引用,那么Entry里面存储的Object,并没有办法进行回收,所以ThreadLocalMap 做了一些额外的回收工作。
ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
想要避免内存泄露就要手动remove()掉。
ThreadLocal的最佳实践!
ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,但是Entry是强引用,那么Entry里面存储的Object,并没有办法进行回收,所以ThreadLocalMap 做了一些额外的回收工作。
备注:很多时候,我们都是用在线程池的场景,程序不停止,线程基本不会销毁!!!
由于线程的生命周期很长,如果我们往ThreadLocal里面set了很大很大的Object对象,虽然set、get等等方法在特定的条件会调用进行额外的清理,但是ThreadLocal被垃圾回收后,在ThreadLocalMap里对应的Entry的键值会变成null,但是后续在也没有操作set、get等方法了。
所以最佳实践,应该在我们不使用的时候,主动调用remove方法进行清理。
这里把ThreadLocal定义为static还有一个好处就是,由于ThreadLocal有强引用在,那么在ThreadLocalMap里对应的Entry的键会永远存在,那么执行remove的时候就可以正确进行定位到并且删除!!!
最佳实践做法应该为:
抽象为:
try {
// 其它业务逻辑
} finally {
threadLocal对象.remove();
}
我对ThreadLocal的一些理解 内存泄露啥的相关推荐
- 使用ThreadLocal不当可能会导致内存泄露
使用ThreadLocal不当可能会导致内存泄露 基础篇已经讲解了ThreadLocal的原理,本节着重来讲解下使用ThreadLocal会导致内存泄露的原因,并讲解使用ThreadLocal导致内存 ...
- ThreadLocal可能引起的内存泄露
threadlocal里面使用了一个存在弱引用的map,当释放掉threadlocal的强引用以后,map里面的value却没有被回收.而这块value永远不会被访问到了. 所以存在着内存泄露. 最好 ...
- 一文搞懂ThreadLocal及相关的内存泄露问题
首先,看一张整体的结构图,来帮助理解 什么是ThreadLocal ThreadLocal用于创建线程局部变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副 ...
- ThreadLocal是否会引发内存泄露的分析 good
这篇文章,主要解决一下疑惑: 1. ThreadLocal.ThreadLocalMap中提到的弱引用,弱引用究竟会不会被回收? 2. 弱引用什么情况下回收? 3. JAVA的ThreadLocal和 ...
- ThreadLocal巨坑!内存泄露只是小儿科
本文将会详细总结 ThreadLocal 容易用错的三个坑: 内存泄露 线程池中线程上下文丢失 并行流中线程上下文丢失 内存泄露 由于 ThreadLocal 的 key 是弱引用,因此如果使用后不调 ...
- c++ socket线程池原理_一篇文章看懂 ThreadLocal 原理,内存泄露,缺点以及线程池复用的值传递问题...
编辑:业余草来源:https://www.xttblog.com/?p=4946 一篇文章看懂 ThreadLocal 原理,内存泄露,缺点以及线程池复用的值传递问题. ThreadLocal 相信不 ...
- java 内存泄漏场景_Java内存泄露的例子
在定位JVM性能问题时可能会遇到内存泄露导致JVM OutOfMemory的情况,在使用Tomcat容器时如果设置了reloadable="true"这个参数,在频繁热部署应用时也 ...
- inputstreamreader未关闭会导致oom_ThreadLocal 一定会导致内存泄露?
在面试的时候,ThreadLocal作为高并发常用工具经常会被问到.而面试官比较喜欢问的问题有以下两个: 1.ThreadLocal是怎么实现来保证每个线程的变量副本的. 2.ThreadLocal的 ...
- threadlocal内存泄露_深入理解 ThreadLocal
前言 上篇文章 https://juejin.im/post/5d712cedf265da03ea5a9ecf 中提到了获取线程的 Looper 是通过 ThreadLocal 来实现的: publi ...
最新文章
- STM32:从菜鸟到牛人就是如此简单!为了学习单片机而去学习单片机的思路是不对的
- POJ 2114 - Boatherds
- OpenGL之正背面剔除、深度测试与多边形偏移
- C. Commentator problem
- mysql 设置时区,【MySQL】修改时区设置
- Build Roads
- java缩写_Java学习-Java缩写词的意思
- Doule类型转成十六进制查看
- MyEclipse Maven 警告: Failed to scan JAR [file:/C:/xxxxx.jar] from WEB-INF/lib
- 折腾Java设计模式之访问者模式
- 计算机游戏制作英文要学好吗,有关怎么不沉迷于电脑游戏的英语作文你的同学MICHAEL沉迷于电脑游戏中,影响了学习.作为好朋友,你要怎么帮助他?写一篇80词左右的英语作文....
- 【优化算法】静电放电优化算法(ESDA)【含Matlab源码 1439期】
- 一元云购 java源码
- android微信朋友圈相册背景,微信朋友圈相册背景多大尺寸合适
- bootloader 解析
- 文件或目录损坏且无法读取的解决办法
- 后退一步 小程序_微信小程序:如何利用navigateBack退出小程序
- 在Geany里配置python3的方法!!!含window10下载Geany过程
- 剑麻的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- C语言:最大公约数。
热门文章
- 【031】◀▶ 一些心得体会总结
- 5种方式将机器学习带到Java、Python以及Go等编程语言
- 在脚本中, 使用sqlite3检查android程序生成的数据库是否OK
- 蓝桥杯 PREV-27 历届试题 蚂蚁感冒
- 蓝桥杯 ADV-141 算法提高 判断名次
- 蓝桥杯 ALGO-39 算法训练 数组排序去重
- 计算机网络 时延、发送时延、传输时延、处理时延、排队时延、时延带宽积
- 【数据库原理】滨江学院姜青山 期末试卷知识点笔记整理 南京信息工程大学
- java设置只有一行表格,为什么我的表格插入一行后 样式都变了?是因为没有设置css吗?如果在java函数中插入的td.innerHTML = input type='text'/,可以设置样...
- Choerodon猪齿鱼敏捷管理实践(一)——需求管理