ThreadLocal部分源码分析和应用场景
结构演进
早起JDK版本中,ThreadLocal内部结构是一个Map,线程为key,线程在“线程本地变量”中绑定的值为Value。每一个ThreadLocal实例拥有一个Map实例。(Key是线程,Value是值)
JDK8中,ThreadLocal内部结构发生了演进,虽然还是Map,但是拥有者变成了Thread实例,每一个Thread实例拥有一个Map实例。Map中的key变为ThreadLocal实例。(Key是ThreadLocal,Value是值)
新版ThreadLocalMap如图:

每一个线程在获取本地值时,都会将ThreadLocal实例作为Key从自己拥有的ThreadLocalMap中获取值,别的线程无法访问自己的ThreadLocalMap实例,达到相互隔离的目的。
ThreadLocal为什么会内存泄漏
我们知道,ThreadLocal是基于ThreadLocalMap实现的,这个Map的Entry继承了WeakReference,而Entry对象中的key使用了WeakReference封装,也就是说Entry中的key是一个弱引用类型,而弱引用类型只能存活在下次GC之前。如果一个线程调用ThreadLocal的set设置变量,当前ThreadLocalMap则新增一条记录,但发生一次垃圾回收,此时key值被回收,而value值依然存在内存中,由于当前线程一直存在,所以value值将一直被引用。这些被垃圾回收掉的key就存在一条引用链的关系一直存在:
Thread --> ThreadLocalMap -->Entry --> Value
这条引用链会导致Entry不会回收,Value也不会回收,但Entry中的Key却已经被回收的情况,造成内存泄漏。其实,ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。
但是这些被动的预防措施并不能保证不会内存泄漏:
使用线程池的时候,这个线程执行任务结束,ThreadLocal对象被回收了,线程放回线程池中不销毁,这个线程一直不被使用,导致内存泄漏。
分配使用了ThreadLocal又不再调用get(),set(),remove()方法,那么这个期间就会发生内存泄漏。
我们只需要在使用完该key值之后,通过remove方法remove掉,就可以防止内存泄漏了。
Entry的Key使用弱引用
为什么不直接使用ThreadLocal实例作为Key呢?
key 使用强引用:引用的ThreadLocal的对象被回收了,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏。
key 使用弱引用:引用的ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set,get的时候会被清除。
比较两种情况,我们可以发现:由于ThreadLocalMap的生命周期跟Thread一样长,如果都没有手动删除对应key,都会导致内存泄漏,但是使用弱引用可以多一层保障:弱引用ThreadLocal不会内存泄漏,对应的value在下 一次ThreadLocalMap调用set,get,remove的时候会被清除。
因此,ThreadLocal内存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏,而不是因为弱引用。
弱引用:对象只能生存到下一次垃圾回收之前。
应用场景
线程隔离
这个的典型应用就是“数据库连接独享”。下面的代码来自Hibernate,代码通过ThreadLoacl进行数据库连接(Session)的线程本地化存储。
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() {
Session s = (Session) threadSession.get();
if(s == null) {
s = getSession();
threadSession.set(s);
}
return s;
}
一个Session代表了一个数据库连接。通过以上代码可以看出,这个Session相当于线程的私有变量,不是所有线程共用的,其他线程是获取不到这个Session的。
一般来说,完成数据库操作之后程序会将Session关闭,节省资源。如果Session为共享的方式,如果某个线程将Session关闭,其他线程在操作Session时就会报错。所以通过ThreadLocal简单实现了数据库连接的安全使用。
Reference
《Java高并发核心编程》
https://www.cnblogs.com/arielmeng/p/15617405.html
ThreadLocal部分源码分析和应用场景的更多相关文章
- 并发编程(四):ThreadLocal从源码分析总结到内存泄漏
一.目录 1.ThreadLocal是什么?有什么用? 2.ThreadLocal源码简要总结? 3.ThreadLocal为什么会导致内存泄漏? 二.ThreadLoc ...
- 【源码分析】cocostudio场景编辑器的触发器逻辑
去看场景编辑器的差不多都可以看到有模拟器的设置(菜单栏的设置).默认是选择cocostudio安装路径中的Simulator.exe这个模拟器,看官网介绍是自己可以选择模拟器,而且公开源代码可以按需设 ...
- ThreadLocal部分源码分析
结构演进 早起JDK版本中,ThreadLocal内部结构是一个Map,线程为key,线程在"线程本地变量"中绑定的值为Value.每一个ThreadLocal实例拥有一个Map实 ...
- ThreadLocal和ThreadLocalMap源码分析
目录 ThreadLocal和ThreadLocalMap源码分析 背景分析 定义 例子 源码分析 ThreadLocalMap源码分析 ThreadLocal源码分析 执行流程总结 源码分析总结 T ...
- 并发编程学习笔记(8)----ThreadLocal的使用及源码分析
1. ThreadLocal的理解 ThreadLocal,顾名思义,就是线程的本地变量,ThreadLocal会为每个线程创建一个本地变量副本,使得使用ThreadLocal管理的变量在多线程的环境 ...
- ThreadLocal源码分析与实践
ThreadLocal是什么? ThreadLocal是一个线程内部存储类,提供线程内部存储功能,在一个ThreadLocal对象中,每一个线程都存储各自独立的数据,互不干扰 示例如下: public ...
- lesson2:java阻塞队列的demo及源码分析
本文向大家展示了java阻塞队列的使用场景.源码分析及特定场景下的使用方式.java的阻塞队列是jdk1.5之后在并发包中提供的一组队列,主要的使用场景是在需要使用生产者消费者模式时,用户不必再通过多 ...
- 02_ThreadLocal语法与源码分析
文章导读: 早在JDK 1.2的版本中就提供Java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路.使用这个工具类可以很简洁地编写出优美的多线程 ...
- Netty源码分析--内存模型(上)(十一)
前两节我们分别看了FastThreadLocal和ThreadLocal的源码分析,并且在第八节的时候讲到了处理一个客户端的接入请求,一个客户端是接入进来的,是怎么注册到多路复用器上的.那么这一节我们 ...
- lesson1:threadlocal的使用demo及源码分析
本文中所使用的demo源码地址:https://github.com/mantuliu/javaAdvance 其中的类Lesson1ThreadLocal 本文为java晋级系列的第一讲,后续会陆续 ...
随机推荐
- sonar使用
代码质量检查工具 sonar 1. 下载,版本sonar 4.5.1 运行bin下的bat文件,浏览器中访问: http://localhost:9000 , 成功. 2. 修改数据库为mysql数据 ...
- [自用初学]c++的构造函数
#include <stdio.h> #include <string.h> class Student { private: int id; char name[32]; p ...
- 音速启动 Vstart 5.7 win10手动移除后台设置主页
Vstart 用了快12年了,用Wireshark跟踪确实会访问广告页面,也会去上传数据.还会悄悄设置主页 ,所以在Win10上老是被干掉 也想着换 CLaunch 确实不喜欢. Rolan 买了一年 ...
- QPushButton与Enter相链接
ui->pushButton_login->setFocus(); // 设置默认焦点 ui->pushButton_login->setShortcut(QKeySequen ...
- 修改tomcat启动时,修改默认访问的页面
在
- binlog2sql 实战心得
原创:binlog2sql在GitHub的地址:https://github.com/danfengcao/binlog2sql 作者:danfengcao 功能:从MySQL binlog解析出你要 ...
- 线程池使用、countDownLatch、以及数据库批量插入 添加配置优化插入与计算
//新建线程池ThreadPoolExecutor cpuThreadPoolExecutor = ThreadUtil.getCpuThreadPoolExecutor(); //使用Countdo ...
- StarUML画用例图
好久没画图在StarUML里都找不到useCase了,记下来不然又忘了 右击Untitled->Add Diagram->UseCase Diagram,然后左边就有UseCase了就可以 ...
- 时序图,E-R图,数据流程图
其中OSC是在线客服 数据流程图
- C语言初级阶段7——指针4
C语言初级阶段7--指针4 结构体指针 1.概念:所谓的结构体指针就是指向结构体变量的指针,一个结构体变量的起始地址就是这个结构体变量的指针.如果把一个结构体变量的其实地址存放在一个指针变量中,那么这 ...