ThreadLocal原理

简介: 经典八股文之ThreadLocal原理

Java相关文章


ThreadLocal原理

  1. ThreadLocal对象new出来存放到堆中,ThreadLocal引用是存放在栈里
  2. Thread 类有个 ThreadLocalMap 成员变量,Map的key是Threadlocal 对象,value是你要存放的线程局部变量。
publicvoidset(Tvalue) {
//获取当前线程Thread,就是上图画的Thread 引用Threadt=Thread.currentThread(); 
//Thread类有个成员变量ThreadlocalMap,拿到这个MapThreadLocalMapmap=getMap(t);
if (map!=null)
//this指的就是Threadlocal对象map.set(this, value);
elsecreateMap(t, value);
}
ThreadLocalMapgetMap(Threadt) {
//获取线程的ThreadLocalMapreturnt.threadLocals;
}
voidcreateMap(Threadt, TfirstValue) {
//初始化t.threadLocals=newThreadLocalMap(this, firstValue);
}
  1. ThreadLocalMap 类的定义在 Threadlocal中。结构与HashMap一样
  2. 使用ThreadLocal后一定手动remove掉
  1. 引用ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set、get、remove的时候会被清除。
  1. ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。


既然是线程局部变量,那为什么不用线程对象(Thread对象)作为key,这样不是更清晰,直接用线程作为key获取线程变量?

  1. 这样设计会有个问题,比如: 我已经把用户信息存在线程变量里了,这个时候需要新增加一个线程变量,比方说新增用户地理位置信息
  2. 我们ThreadlocalMap 的key用的是线程,再存一个地理位置信息,key都是同一个线程(key一样),会覆盖原来的用户信息。


为什么ThreadLocal中的key设计成WeakReference

ThreadLocal<UserInfo>userInfoThreadLocal=newThreadLocal<>();
userInfoThreadLocal.set(userInfo);
  1. 为了尽大可能的避免内存泄漏
  2. new出的ThreadLocal被userInfoThreadLocal强引用,同时也被ThreadLocalMap的Key弱引用
  3. gc要回收ThreadLocal对象的前提是它只被WeakReference引用
  4. 所以只要ThreadLocal对象如果还被 userInfoThreadLocal(强引用) 引用着,GC是不会回收被WeakReference引用的对象的。
  5. 有些线程会用线程池优化,线程生命周期很长,根据JVM 根搜索算法,一直存在 Thread -> ThreadLocalMap -> Entry(元素)这样一条引用链路,如果key不设计成WeakReference类型,是强引用的话,就一直不会被GC回收,key就一直不会是null,不为null Entry元素就不会被清理(ThreadLocalMap是根据key是否为null来判断是否清理Entry)
  6. 所以ThreadLocal的设计者认为只要ThreadLocal 所在的作用域结束了工作被清理了,GC回收的时候就会把key引用对象回收,key置为null,ThreadLocal会尽力保证Entry清理掉来最大可能避免内存泄漏。
  7. ThreadLocalMap中Entry 继承了WeakReference类,Entry 中的 key 是WeakReference类型的,在Java 中当对象只被 WeakReference 引用,没有其他对象引用时,被WeakReference 引用的对象发生GC 时会直接被回收掉。




相关文章
mac上datagrip.vmoptions文件编辑错误导致DataGrip软件打不开
mac上datagrip.vmoptions文件编辑错误导致DataGrip软件打不开
|
自然语言处理 Java 索引
Elastic:如何查询特殊字符
某些业务场景下我们需要使用特殊符号来进行查询,但是es的默认分词器以及ik分词器等大多数分词器都会将特殊符号过滤掉,导致后续无法通过特殊符号查询到数据。 那么我们如何来解决这个问题呢,下面列举出几种处理方案
1431 0
|
Java 中间件 微服务
27个阿里 Java 开源项目,值得收藏!
大家好,这里为大家整理了阿里的Java开源项目,希望对大家有所帮助
12732 0
27个阿里 Java 开源项目,值得收藏!
|
10月前
|
安全 Java C#
使用try-with-resources实现自动解锁
本文介绍了如何利用Java的`try-with-resources`语法糖实现Redission分布式锁的自动解锁。通过将锁包装为实现了`AutoCloseable`接口的类,在`try`语句块结束时自动释放锁,简化了锁管理,避免了手动解锁的繁琐和潜在错误。示例代码展示了具体的实现方法,并分析了其优点和注意事项。这种模式提高了代码的简洁性、可靠性和可维护性,特别适用于多线程编程中的并发控制。
224 5
|
存储 算法 安全
深入详解ThreadLocal
在我们日常的并发编程中,有一种神奇的机制在静悄悄地为我们解决着各种看似棘手的问题,它就是 ThreadLocal 。
21934 9
深入详解ThreadLocal
如何给你的Idea换一个好看漂亮的主题
如何给你的Idea换一个好看漂亮的主题
|
Java Spring
【新手指南】严重: Exception sending context initialized event to listener instance of class
【新手指南】严重: Exception sending context initialized event to listener instance of class
907 0
【新手指南】严重: Exception sending context initialized event to listener instance of class
|
Java Windows
vscode 配置 plantuml
vscode 配置 plantuml
vscode 配置 plantuml
|
消息中间件 存储 JavaScript
ThreadLocal 搭配线程池使用造成内存泄漏的原因和解决方案
ThreadLocal 搭配线程池使用造成内存泄漏的原因和解决方案
|
SQL Oracle 关系型数据库
Oracle 到OceanBase 数据迁移OMS最佳实践
本文介绍Oracle 到OceanBase 数据迁移OMS最佳实践。
1265 0