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晋级系列的第一讲,后续会陆续 ...
随机推荐
- x-www-form-urlencoded请求封装
<dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpc ...
- 北大2022编译原理实践(C/C++)-sysy 编译器构建
这是今年新推出的实践方案,由往年的sysy->IR1->IR2->RISC V变成了sysy->Koopa->RISC V,通过增量的方式让整个实践过程更容易上手 所以先 ...
- Java基础|01.基础语法(1)
目录 00x1 基本语法 1.类的语法 2.对象的语法 3.方法的声明 4.小例子 00x2 类和对象的关系 1.堆.栈和元空间 2.基本数据类型和引用数据类型的区别 3. 空对象(null) 00x ...
- 20200926--矩阵转置(奥赛一本通P95 8 多维数组)
输入一个n行m列的矩阵A,输出它的转置(看下面说明) 输入:第1行包含两个整数n和m(1<=n<=100,1<=m<=100),表示矩阵A的行数和列数.接下来n行,每行m个整数 ...
- AntD为Form的List设置默认值 (antd form.list 设置默认值 )
import React from "react"; function demo() { const FormConfig = { labelCol: { span: 8 }, w ...
- 个人PSP(四则运算)升级
源代码管理平台Gitbee地址:https://gitee.com/chen-haijin/ 1.题目要求:能自动生成小学四则运算题目,且每一道题目的运算结果不能为负.除了支持整数运算外,还要支持真分 ...
- Python 爬虫代码应该怎么写?
对于入行已久的老程序员也并不一定精通爬虫代码,这些需要时间的沉淀还需要更多的实战案例,简单的问句你真的会写爬虫么?下面就是我日常写的一个y文件加上几个请求并且把需要的功能全部实现模块化,可以让我们爬虫 ...
- 20181224蒋嘉豪-exp5
网络对抗技术Exp5 信息搜集与漏洞扫描20181224jjh 目录 网络对抗技术Exp5 信息搜集与漏洞扫描20181224jjh 信息搜集技术与隐私保护--知识点总结 间接收集 直接收集 社会工程 ...
- localtime函数
localtime函数: 将时间数值变换成本地时间,考虑到本地时区和夏令时标志; 原型: struct tm *localtime(const time_t * calptr); 头文件 <ti ...
- kubeadm搭建k8s
一.kubeadm 部署 K8S 集群架构 主机名 IP地址 安装组件 master(2C/4G,cpu核心数要求大于2) 192.168.160.20 docker.kubeadm.kubelet. ...