每日一题之 ThreadLocal

点击上方 Java后端选择 设为星标html

优质文章,及时送达java


ThreadLocal的原理和实现

ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不一样的 Thread 中有不一样的副本。ThreadLocal 变量一般被private static修饰。当一个线程结束时,它所使用的全部 ThreadLocal 相对的实例副本均可被回收。web

一个线程内能够存在多个 ThreadLocal 对象,因此实际上是 ThreadLocal 内部维护了一个 Map ,这个 Map 不是直接使用的 HashMap ,而是 ThreadLocal 实现的一个叫作 ThreadLocalMap 的静态内部类。面试

而咱们使用的 get()、set() 方法其实都是调用了这个ThreadLocalMap类对应的 get()、set() 方法。这个储值的Map并不是ThreadLocal的成员变量,而是java.lang.Thread 类的成员变量。ThreadLocalMap实例是做为java.lang.Thread的成员变量存储的,每一个线程有惟一的一个threadLocalMap。数据库

这个map以ThreadLocal对象为key,”线程局部变量”为值,因此一个线程下能够保存多个”线程局部变量”。对ThreadLocal的操做,实际委托给当前Thread,每一个Thread都会有本身独立的ThreadLocalMap实例,存储的仓库是Entry[] table;Entry的key为ThreadLocal,value为存储内容;所以在并发环境下,对ThreadLocal的set或get,不会有任何问题。后端

因为Tomcat线程池的缘由,我最初使用的”线程局部变量”保存的值,在下一次请求依然存在(同一个线程处理),这样每次请求都是在本线程中取值。因此在线程池的状况下,处理完成后主动调用该业务treadLocal的remove()方法,将”线程局部变量”清空,避免本线程下次处理的时候依然存在旧数据。微信

ThreadLocal为何要使用弱引用和内存泄露问题数据结构

在ThreadLocal中内存泄漏是指ThreadLocalMap中的Entry中的key为null,而value不为null。由于key为null致使value一直访问不到,而根据可达性分析致使在垃圾回收的时候进行可达性分析的时候,value可达从而不会被回收掉,可是该value永远不能被访问到,这样就存在了内存泄漏。架构

若是 key 是强引用,那么发生 GC 时 ThreadLocalMap 还持有 ThreadLocal 的强引用,会致使 ThreadLocal 不会被回收,从而致使内存泄漏。弱引用 ThreadLocal 不会内存泄漏,对应的 value 在下一次 ThreadLocalMap 调用 set、get、remove 方法时被清除,这算是最优的解决方案。并发

Map中的key为一个threadlocal实例.若是使用强引用,当ThreadLocal对象(假设为ThreadLocal@123456)的引用被回收了,ThreadLocalMap自己依然还持有ThreadLocal@123456的强引用,若是没有手动删除这个key,则ThreadLocal@123456不会被回收,因此只要当前线程不消亡,ThreadLocalMap引用的那些对象就不会被回收,能够认为这致使Entry内存泄漏。

若是使用弱引用,那指向ThreadLocal@123456对象的引用就两个:ThreadLocal强引用和ThreadLocalMap中Entry的弱引用。一旦ThreadLocal强引用被回收,则指向ThreadLocal@123456的就只有弱引用了,在下次gc的时候,这个ThreadLocal@123456就会被回收。

虽然上述的弱引用解决了key,也就是线程的ThreadLocal能及时被回收,可是value却依然存在内存泄漏的问题。当把threadlocal实例置为null之后,没有任何强引用指向threadlocal实例,因此threadlocal将会被gc回收.map里面的value却没有被回收.而这块value永远不会被访问到了. 

因此存在着内存泄露,由于存在一条从current thread链接过来的强引用.只有当前thread结束之后, current thread就不会存在栈中,强引用断开, Current Thread, Map, value将所有被GC回收.因此当线程的某个localThread使用完了,立刻调用threadlocal的remove方法,就不会发生这种状况了。

另外其实只要这个线程对象及时被gc回收,这个内存泄露问题影响不大,但在threadLocal设为null到线程结束中间这段时间不会被回收的,就发生了咱们认为的内存泄露。最要命的是线程对象不被回收的状况,这就发生了真正意义上的内存泄露。好比使用线程池的时候,线程结束是不会销毁的,会再次使用,就可能出现内存泄露。

- END -
       
最近整理一份面试资料《Java技术栈学习手册》,覆盖了Java技术、面试题精选、Spring全家桶、Nginx、SSM、微服务、数据库、数据结构、架构等等。
获取方式:点“ 在看,关注公众号 Java后端 并回复 777 领取,更多内容陆续奉上。
读 
1. 每日一题之 ZooKeeper
2. 每日一题之 Redis
3. 
每日一题之 MySQL

 
        
        
        
         
         
                  
         
 
        
4. 
每日一题之 JVM-01

5.  每日一题之 JVM-02



6
.

 
每日一题之 JVM-03


7. 

每日一题之线程


8.

  每日一题之 HashMap

  
         
         
         
          
          
                   
          
  
         
9.
 每日一题之 HashMap - 02



在看 

本文分享自微信公众号 - Java后端(web_resource)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索