解析ThreadLocal

ThreadLocal是在开发中相对比较常见的工具类了,可以在多线程环境下保证线程安全,其基本原理就是给每一个对象都分配一个属于当前线程的私有对象,这样线程之间拿到的对象就不会相互冲突了。

可以想象成,如果多个人公用一个厕所的时候,因为厕所的一些限制,有很多的人都不能冒然的使用厕所,如果每一个人来的时候都给他一个单独的厕所这样相互之间就不会打扰了,缺点的话也很明显可以看出来就是浪费资源。所以在用完的时候要记得销毁资源。

有的人可能会问你再什么时候下使用ThreadLocal呢?

  • 在Spring的事务中,默认内部用的是ThreadLocal给每一个线程分配一个单独的连接,不过这是内部已经封装好的了,不算是我们自己写的
  • 在Web应用中,有的时候要对一些请求做单独的处理,响应做额外的处理,每个人的写法都不一样,我有见过一些人在每一个请求的时候对应一个线程,并且对每个请求都有一些ThreadLocal相关的变量,存储请求的信息,以便在后续的处理中再从ThreadLocal中去拿
  • 比较常见的例子,应该是SimpleDateFormat了,这个对象在多线程下会出现一定的问题,一般在高并发的场景下,都会使用ThreadLocal给每个线程分配一个SimpleDateFormat对象。

知道了原理在使用的时候考虑下,或者在对应的场景下想想能不能用ThreadLocal去做。

内部结构

只是知道了其基本使用的话,相对还是不够,其内部的数据结构也很有意思,在看过之后才发现其中的巧妙。

  • 每个Thread对象内部都有一个ThreadLocal.ThreadLocalMap属性,属性名为threadLocals,在创建线程之后可以直接通过t.threadLocals访问
image-20190513101303375

当调用ThreadLocal的set方法时:

  • 获取当前Thread的ThreadLocalMap属性,如果当前线程的ThreadLocalMap为空的话,会进行初始化并赋值,如果不为空,则直接赋值

当有多个ThreadLocal对象时候,其实每个线程内部的ThreadLocalMap属性里面的Entry数组,对应的下标为ThreadLocal的hashCode进行取余,然后再构建Entry对象。

其实就是一个线程内部可以存储多个ThreadLocal给的线程私有对象,只不过ThreadLocal对象是访问那些线程私有对象的入口。

内存泄漏问题

关于ThreadLocal如果不进行Remove是否会导致内存泄漏?

通过上面我们知道Thread中的ThreadLocalMap属性中的Entry表都是弱引用的对象,其引用为ThreadLocal对象,那么弱引用对象一般在进行垃圾回收的时候都会被回收掉。

ThreadLocal在没有外部强引用时,发生GC时会被回收,如果创建ThreadLocal的线程一直持续运行,那么这个Entry对象中的value就有可能一直得不到回收,发生内存泄露。

记得在使用ThreadLocal完毕之后,调用ThreadLocal.remove方法。

最后

ThreadLocal也算是一个比较经典的知识点了,通过它可以问出

  • 引用类型
  • Map的数据结构
  • 线程安全
  • 实际场景中的用法

希望对你有帮助

参考

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 移步Android Handler机制详解[https://www.jianshu.com/p/e37e2db2b...
    凯玲之恋阅读 870评论 0 0
  • 前言 本文将以一个例子开头简单看看ThreadLocal类的特性,进而分析该类的源代码. 本文源码下载 例子 启动...
    nicktming阅读 450评论 0 1
  • 原文链接:Java进阶(七)正确理解Thread Local的原理与适用场景 - 郭俊Jason - 博客园 Th...
    Walter_Hu阅读 795评论 0 1
  • 原理 产生线程安全问题的根源在于多线程之间的数据共享。如果没有数据共享,就没有多线程并发安全问题。ThreadLo...
    Java耕耘者阅读 306评论 0 0
  • 分班后第一天,新的教室,新的同学,老师。 昨天老师有提醒别走错教室,今天有别的班的同学走错了,因为很多都不认识,所...
    2019影阅读 241评论 0 0