用Java语言开发的同学对 ThreadLocal 应该都不会陌生,这个类的使用场景很多,特别是在一些框架中经常用到,比如数据库事务操作,还有MVC框架中数据跨层传递。这里我们简要探讨下 ThreadLocal 的内部实现及可能存在的问题。

首先问自己一个问题,让自己实现一个这个的功能类的话怎么去做?第一反应就是简单构造一个 Map<Thread, T> 数据结构,key是 Thread,value就是我们要保存的线程变量 T。我们看下这种设计有哪些问题:

  • 随着运行时间越久,存在Map里的Thread越多,当Thread退出时,资源也没有释放,存在内存泄漏问题
  • Map数据因为会被多线程访问,存在资源竞争,所以还必需对Map做同步安全操作,效率低下

JDK中的 ThreadLocal 精妙的设计来解决问题上述两个问题。首先每个Thread(线程)内部都有一个Map结构数据ThreadLocalMap<ThreadLocal, T>,当我们对线程变量赋值时ThreadLocal.set(T value)时,其实是先获取当前线程Thread.currentThread())的内部属性字段ThreadLocalMap,然后以当前ThreadLocal为key设置线程变量值T。这种设计的精髓是,每个Thread线程都维护一份自己的ThreadLocalMap数据结构,这样就解决了上面所述问题中的第二个,不存在竞争条件。

既然每个Thread内部都维护一个ThreadLocalMap字典数据结构,字典的Key值是ThreadLocal,那么当某个ThreadLocal对象不再使用(没有其它地方再引用)时,每个已经关联了此ThreadLocal的线程怎么在其内部的ThreadLocalMap里做清除此资源呢?JDK中的ThreadLocalMap又做了一次精彩的表演,它没有继承java.util.Map类,而是自己实现了一套专门用来定时清理无效资源的字典结构。其内部存储实体结构Entry<ThreadLocal, T>继承自java.lan.ref.WeakReference,这样当ThreadLocal不再被引用时,因为弱引用机制原因,当jvm发现内存不足时,会自动回收弱引用指向的实例内存,即其线程内部的ThreadLocalMap会释放其对ThreadLocal的引用从而让jvm回收ThreadLocal对象。这里是重点强调下,是回收对ThreadLocal对象,而非整个Entry,所以线程变量中的值T对象还是在内存中存在的,所以内存泄漏的问题还没有完全解决。接着分析JDK的实现,会发现在调用ThreadLocal.get()或者ThreadLocal.set(T)时都会定期执行回收无效的Entry操作。所以这就解决了上述问题中的第1个问题。

问题真的都解决了吗,好像都解决了。因为即没有竞争资源操作,也不会存在内存泄漏。但是细想一下,总感觉哪里不对劲,真的不会存在内存溢出(OOM)问题吗?上面一段的分析中,强调ThreadLocalMap定期清理内部的无效Entry对象,触发的条件就是对TrheadLocal执行 set,get,remove()等操作时会触发,但是如果存在这样的场景,当我们在某个线程上下文中执行ThreadLocal.set(T)设置了一个很大内在的数据结构,然后该ThreadLocal被清除引用回收,之前的线程又一直存活着,则这个大内存数据对象T是一直不回收的,这里很容易写个代码测试出OOM来。怎么解决这个问题呢?

Lucene中的org.apache.lucene.util.CloseableThreadLocal类解决了上述特殊场景引起的问题:即解决JDK中因为定期才执行无效对象回收的问题。CloseableThreadLocal在内部维护了一个ThreadLocal,当执行CloseableThreadLocal.set(T)时,内部其实只是代理的把值赋给内部的ThreadLocal对象,即执行ThreadLocal.set(new WeakReference(T))。看到这里应该明白了,这里不是直接存储T,则是包装成弱引用对象,目的就是当内存不足时,jvm可以回收此对象。但是细心的你会发现会引入一个新的问题,即当前线程还存活着的时候,因为内存不足而回收了弱引用对象,这样会在下次调用get()时取不到值返回null,这是不可接受的。所以CloseableThreadLocal在内部还创建了一个数据,WeakHashMap<Thread, T>,当线程只要存活时,则T就至少有一个引用存在,所以不会被提前回收。但是又引入的第2个问题,对WeakHashMap的操作要做同步synchronized限制。你看,所有的东西都不是十全十美的,我们掌握那个平衡点就行了。

谈谈ThreadLocal的设计及不足的更多相关文章

  1. 谈谈UI架构设计的演化

    谈谈UI架构设计的演化 经典MVC 在1979年,经典MVC模式被提出. 在当时,人们一直试图将纯粹描述思维中的对象与跟计算机环境打交道的代码隔离开来,而Trygve Reenskaug在跟一些人的讨 ...

  2. Java中ThreadLocal的设计与使用

    早在Java 1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择.使用这个工具类可以很简洁地编写出优美的多线程程 ...

  3. ThreadLocal的设计与使用(原理篇)

    在jdk1.2推出时开始支持java.lang.ThreadLocal.在J2SE5.0中的声明为:            public class ThreadLocal<T> exte ...

  4. 转!! Java中ThreadLocal的设计与使用

    首先,ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的.各 ...

  5. 结合源码谈谈ThreadLocal!

    目录 ThreadLocal的作用 ThreadLocal 1.对象初始化 2.获取变量 3.设置变量 4.移除变量 ThreadLocalMap 1.Entry 2.初始化 3.获取Entry 4. ...

  6. 谈谈ThreadLocal的应用场景和注意事项?

    特点 ThreadLocal和Sychronized都用于解决多线程间的并发访问,但它们实现的本质方法不同:sychronized利用锁使同一个代码块或变量在某时刻只能被一个线程访问,而ThreadL ...

  7. 【MySQL】谈谈PhxSQL的设计和实现哲学

    参考资料: http://mp.weixin.qq.com/s?__biz=MzI4NDMyNTU2Mw==&mid=2247483790&idx=1&sn=c925202df ...

  8. 谈谈Java中的ThreadLocal

    什么是ThreadLocal ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本.通过ThreadLocal可以将对象的 ...

  9. 我是如何理解ThreadLocal

    ThreadLocal的概念 ThreadLocal从英文的角度看,可以看成thread和local的组合,就是线程本地的意思,我们都知道,看过jvm内存分配的人都知道在jvm虚拟机中对每一个线程都分 ...

随机推荐

  1. mysql 的一些事

    给mysql的root用户设置密码 1.刚安装好的mysql是没有设置密码的 2.设置密码 ****************************************************** ...

  2. axure元件库导入后重启程序元件库消失问题

    把元件库文件放在我的文档里的 \Documents\Axure\元件库 就可以了,重新启动程序不会消失

  3. Linksys EA6500刷ddwrt成功记

    网上刷Linksys EA6500的资料不多,然后又绕了好多个弯子,自己记录备忘. 首先EA6500有两个版本v1和v2,对应的固件不同. 区分方法: 1.v1的背后是两个颜色一样的usb2.0 2. ...

  4. Linux的命名空间详解--Linux进程的管理与调度(二)【转】

    Linux Namespaces机制提供一种资源隔离方案. PID,IPC,Network等系统资源不再是全局性的,而是属于特定的Namespace.每个Namespace里面的资源对其他Namesp ...

  5. Postgresql - jsonb_pretty & dateStyle

    1. SHOW datestyle; DateStyle ----------- ISO, MDY(1 row) INSERT INTO container VALUES ('13/01/2010') ...

  6. syslog与rsyslog的了解与比较

    syslog日志收集器: syslog是早期的centos版本的日志收集器,应该是centos5之前的版本. syslog的两个重要的守护进程: 1.syslogd:system.主要以收集系统服务为 ...

  7. rsync远程同步的基本配置与使用

    rsync是一个开源的快速备份工具,可以在不同主机之间镜像同步整个目录树,支持增量备份,保持链接和权限,且采用优化的同步算法,传输前执行压缩,因此非常适用于异地备份,镜像服务器等应用. rsync的官 ...

  8. SAP ABAP 如何查找SMOD增强

    1.查找程序名 T-CODE:SE93 2.查找开发类 T-code:se38 3.查找SMOD增强 T-CODE:SE16N.表:TADIR 4.查看增强具有哪些功能 T-CODE:SE16N.表: ...

  9. (转)postgresql+postgis空间数据库使用总结

    转载地址:https://blog.csdn.net/qq_36588972/article/details/78902195 参考资料: pgrouting路径导航 https://www.cnbl ...

  10. 寒假集训——搜索 B - Sudoku

    #include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream&g ...