ThreadLocal解析
ThreadLocal是一个存储线程本地变量的对象,在ThreadLocal中存储的对象在其他线程中是不可见的,本文介绍ThreadLocal的原理。
1、threadLocal使用
有如下代码:
@Slf4j
public class TestThreadLocal {public static void main(String[] args) {ThreadLocal<Integer> threadLocal = new ThreadLocal<>();threadLocal.set(999);log.info(threadLocal.get().toString());//使用线程池创建一个线程ExecutorService service = Executors.newSingleThreadExecutor();service.execute(()->{log.info(threadLocal.get().toString());//threadLocal.get()将为nullthreadLocal.set(888);log.info(threadLocal.get().toString());});log.info(threadLocal.get().toString());}
}
输出:
2023-03-09 09:03:40.572 [main] INFO -- 999
2023-03-09 09:03:40.598 [main] INFO -- 999
Exception in thread "pool-1-thread-1" java.lang.NullPointerExceptionat com.iwat.arithmetic.thread.TestThreadLocal.lambda$main$0(TestThreadLocal.java:24)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)
从结果中可以看到,在新线程中无法获取到999,祝线程中set的数,只有祝线程能get到,这就保证了变量的线程唯一。
2、原理
原理就要看源码
首先看threadLocal.set(999);的源码
//ThreadLocal类中public void set(T value) {//1.获取当前线程Thread t = Thread.currentThread();//2. 获取线程中的ThreadLocalMap对象ThreadLocalMap map = getMap(t);if (map != null)//线程中的ThreadLocalMap对象不为空,直接set值,this就是当前ThreadLocal对象没,值就是值map.set(this, value);else//线程中的ThreadLocalMap对象为空,创建并set值createMap(t, value);}
很简单,获取当前线程,然后获取当前线程的ThreadLocalMap对象,这里的ThreadLocalMap可以就当做一个Map处理,但是这个Map不一样的地方在于key只能是ThreadLocal对象。现在我们就可以分析一下了。

每一线程都有一个ThreadLocalMap对象,对于每一个ThreadLocal对象来说它可以在每一个线程中ThreadLocalMap中存一个以它为key的值。例如:
- 在线程1中,ThreadLocal对象1执行set方法,实际上就是在线程1中的ThreadLocalMap中添加一个k-v(1号红色箭头),k就是ThreadLocal对象1。
- 在线程1中,ThreadLocal对象2执行get方法,就是在线程1的ThreadLocalMap中获取以ThreadLocal对象2为key的值(2号红色箭头)
每个ThreadLocal在某个线程中只能有一个对应value,因为Map中key不能重复,这就实现了线程唯一变量的功能。
下面看一下ThreadLocal中的get方法验证一下分析:
//ThreadLocal类中public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}
同时这也解释了,为什么上面代码中出现Exception in thread "pool-1-thread-1" java.lang.NullPointerException,原因就是在新创建的线程中的ThreadLocalMap中没有以当前ThreadLocal对象为key的值。
3、java引用类型
3.1 GC回收对象
java垃圾回收器多采用可达性分析算法来确定一个java对象需不需要回收,过程是这样的:
- 首先确定一些一定不能回收的对象,叫做GCRoot对象
- 查找GCRoot对象引用的对象
- 然后沿着引用链一直找(注意:引用有多种类型)
最终根据对象被引用的类型、有没有被引用,来决定是不是回收这个对象。
3.2 引用类型
那么引用类型有哪些呢?总体而言分为四种:
- 强引用
沿着GC Root引用链可以找到的对象就是强引用对象 - 软引用 (SoftReference)
垃圾回收后仍内存不足,就会回收掉 - 弱号1用 (WeakReference)
垃圾回收就将其回收掉 - 虚引用 (PhantomReference)
必须配合引用队列使用,主要配合 ByteButfer 使用,被引用对象回收时,会将虛引用入队,由
Reference Handler 线程调用虚引用相关方法释放直接内存

4、内存泄漏问题
在看了java中几种引用类型之后在看内存泄漏问题,什么是内存泄漏?就是指一部分对象一直占用内存,也清不掉,就好像这块内存空间没了,漏掉了。
其实在ThreadLocalMap的内部使用的是Entry类存储k-v
static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}/*** The initial capacity -- MUST be a power of two.*/private static final int INITIAL_CAPACITY = 16;//省略若干代码
}
可以看到Entry在实现时继承了弱引用,为什么这样呢?看注释是这样说的
The entries in this hash map extend WeakReference, using its main ref field as the key (which is always a ThreadLocal object). Note that null keys (i.e. entry.get() == null) mean that the key is no longer referenced, so the entry can be expunged from table. Such entries are referred to as "stale entries" in the code that follows.Note that null keys (i.e. entry.get()* == null)
如果 key threadlocal 为 null 了,这个 entry 就可以清除了。
ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收 。
在ThreadLocal的使用时,线程的ThreadLocalMap中都存储了以ThreadLocal为key的值,如果ThreadLocal被清理了,那么
ThreadLocalMap中对应的数据会被清理吗?
并不会,原因是ThreadLocal被清理变成null之后,ThreadLocalMap被Thread所引用(强引用)并不会回收,只是key变成了null。
如何解决呢内存泄漏呢?最简单的办法就是用完之后remove,ThreadLocal不用了,就去ThreadLocalMap中清理到对用的k-v。
相关文章:
ThreadLocal解析
ThreadLocal是一个存储线程本地变量的对象,在ThreadLocal中存储的对象在其他线程中是不可见的,本文介绍ThreadLocal的原理。 1、threadLocal使用 有如下代码: Slf4j public class TestThreadLocal {public static void main(String[] args…...
时间格式表
时间格式化对照表 仅供参考标识符含义aAM/PM(上午/下午)A0~86399999 (一天的第A微秒)c/cc1~7 (一周的第一天, 周天为1)cccSun/Mon/Tue/Wed/Thu/Fri/Sat (星期几简写)ccccSunday/Monday/Tuesday/Wednesday/Thursday/Friday/Saturday (星期几全拼)d1~31 (月份的第几天, 带0)D1~36…...
enscape和twinmotion哪个好用?
Twinmotion 和 Enscape这2款渲染软件最近受到了一些初学者的关注。这 2 个软件适用于那些需要 3D 渲染但质量不是他们项目的首要任务的人。在本文中,我们将对Twinmotion 和 Enscape 进行面对面的比较,并帮助您确定哪一个更适合您。什么是 Twinmotion&…...
Canvas
canvas介绍 什么是 Canvas?Canvas 是为了解决 Web 页面中只能显示静态图片这个问题而提出的,一个可以使用 JavaScript 等脚本语言向其中绘制图像的 HTML 标签。 Canvas 解决了什么问题 我在 MSDN(《Microsoft Developer Network》是微软一…...
旅游预约APP开发具有什么优势和功能
旅游活动目前正在作为广大用户休闲娱乐的一个首选内容,不仅是公司团建活动可以选择旅游,而且一些节假日也可以集结自己的亲朋好友来一次快乐有趣的旅游活动,随着当代人对于旅游的需求呈现上升的趋势,也让旅游预约APP开发开始流行并…...
Python之函数参数细讲
文章目录前言一、了解形式参数和实际参数1. 通过作用理解2. 通过一个比喻来理解形式参数和实际参数二、位置参数1. 数量必须与定义时一致2. 位置必须与定义时一致三、关键字参数四、为参数设置默认值五、可变参数1. *parameter2. **parameter总结前言 在调用函数时,…...
跑步耳机入耳好还是不入耳好、十大跑步运动耳机品牌排行榜推荐
健身房经常会播放一些节奏较快的歌曲,这样能够激发大家在运动过程中的动力,所以运动时聆听音乐确实比较有效果,居家运动、室外跑步时选择运动耳机就变成了刚需,首先不能影响其他人、佩戴时要稳定,音质和续航要有保证&a…...
Go语言容器之数组和切片
Go语言的容器分为值类型和引用数据类型 一、数组 1.数组的声明和初始化 (1) 数组声明的语法 var 数组变量名 [数组大小]数组类型 举例: package main import "fmt"func main(){//数组的声明var arr[10]int//打印数组长度fmt.Println("arr的长度为…...
【ROS2知识】humble下使用插件编程
Creating and using plugins (C++) — ROS 2 Documentation: Humble documentation 一、说明 接口编程的好处不言自明,有兴趣的朋友可以看看相关文章。此处在ROS2上进行接口编程,这是个技术难点,如果不能突破,那么许多方面将不能进行,比如:navigation中的costmap_2d包中…...
MySQL 主备一致
MySQL 主备一致主备切换binlog 格式statementrowmixed生产格式循环复制问题主备切换 MySQL 主备切换流程 : 状态 1 : 客户端的读写都直接访问节点 A,而节点 B 是 A 的备库,只将 A 的更新都同步过来 , 并本地执行。来保持节点 B 和 A 的数据是相同当切换…...
玩转CodeQLpy之用友GRP-U8漏洞挖掘
0x01 前言CodeQLpy是作者使用python3实现的基于CodeQL的java代码审计工具,github地址https://github.com/webraybtl/CodeQLpy。通过CodeQLpy可以辅助代码审计人员快速定位代码中的问题,目前支持对SprintBoot的jar包,SpringMVC的war包…...
GMP调度模型总结
优秀文章 什么是GMP调度模型 Golang的一大特色就是Goroutine。Goroutine是Golang支持高并发的重要保障。Golang可以创建成千上万个Goroutine来处理任务,将这些Goroutine分配、负载、调度到处理器上采用的是G-M-P模型。 什么是Goroutine Goroutine Golang Coro…...
蓝桥回文日期题
题目 题目描述 2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期。 有人表示 20200202 是 “千年…...
【2023】某python语言程序设计跟学第三周内容
目录1.数字类型与操作:整数:浮点数:复数数值运算操作符数字之间关系数值运算函数2.案例:天天向上的力量第一问:1‰的力量第二问:5‰和1%的力量第三问:工作日的力量第四问:工作日的努…...
c++11右值引发的概念
右值引用右值&&左值c11增加了一个新的类型,右值引用,记作:&&左值是指在内存中有明确的地址,我们可以找到这块地址的数据(可取地址)右值是只提供数据,无法找到地址(不…...
MySQL 02 :三层结构、备份删除数据库
MySQL 02 :数据库三层结构-破除MySQL神秘 请添加图片描述 通过golang操作MySQL 创建删除数据库 备份恢复数据库 第一次需要配置环境,否则会报错 报错:mysqldump: Got error: 1045: Access denied for user ‘root’‘localhost’ (using …...
质量员错题合集
项目部质量员根据规范要求认为,接地用的绝缘铜电线规定最小截面为( )mm。4 项目部质量员根据规范要求认为,接地用的绝缘铜电线规定最小截面为4mm,是从( )性能考虑的。机械、 案例中所使用的ZST型闭式喷头的工作压力是( )MPa。1.2 案例中所…...
请教大神们,pmp考试和复习有什么攻略诀窍吗?
PMP考试通过率挺高的,很多考生也是朝九晚五甚至天天加班的打工人,还是有很多人通过了的,我也是下班后和周末才有时间学习的,3A通过,但不是什么考试大神,每天抽出3-4个小时跟着培训机构制定的学习计划学习&a…...
Go语言基础之接口
Go语言基础之接口1.Go语言接口类型2.类型与接口的关系一个类型实现多个接口多种类型实现同一接口3.空接口4.类型断言1.Go语言接口类型 每个接口类型由任意个方法签名组成,接口的定义格式如下: type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列…...
【Go自学第一节】GoLang 数据类型
和Java类型,go拥有多种数据类型,可以把它分为四个大类基础类型、聚合类型、引用类型和接口类型 一、基本数据类型 基本数据类型又可以细分为:数字类型(整型、浮点型)、布尔类型、字符串类型 整型 Go 的整型分为有符号…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化
是不是受够了安装了oracle database之后sqlplus的简陋,无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话,配置.bahs_profile后也能解决上下翻页这些,但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可,…...
