ThreadLocal源码分析
文章目录
- 1.核心数据结构 ThreadLocalMap
- 1.静态内部类 Entry
- 2.真正存储数据的是table数组
- 2.ThreadLocal.set()方法源码详解
- 1.set
- 2.getMap
- 3.ThreadLocalMap.set
- 4.createMap
- 5.rehash
- 6.resize
- 3.ThreadLocalMap.get()详解
- 1.get
- 2.ThreadLocalMap.getEntry
- 3.getEntryAfterMiss
- 4.ThreadLocalMap过期 key 的探测式清理流程
- 5.InheritableThreadLocal原理
1.核心数据结构 ThreadLocalMap
1.静态内部类 Entry
// 继承了弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {// value就是平常定义ThreadLocal中存的那个东西Object value;// key就是ThreadLocal(是弱引用)Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}
}
2.真正存储数据的是table数组

2.ThreadLocal.set()方法源码详解
1.set
public void set(T value) {// 获取当前线程Thread t = Thread.currentThread();// 获取当前线程的ThreadLocalMapThreadLocalMap map = getMap(t);// 如果map不为空,就将当前的ThreadLocal作为key,ThreadLocal存的值作为value设置到map中if (map != null) {map.set(this, value);} else {// 如果map为空,就使用当前线程和ThreadLocal存的值去创建一个mapcreateMap(t, value);}}
2.getMap
ThreadLocalMap getMap(Thread t) {// 返回当前线程的ThreadLocalMapreturn t.threadLocals;}

3.ThreadLocalMap.set
private void set(ThreadLocal<?> key, Object value) {// We don't use a fast path as with get() because it is at// least as common to use set() to create new entries as// it is to replace existing ones, in which case, a fast// path would fail more often than not.// 存储数据的Entry数组Entry[] tab = table;// 数组的长度int len = tab.length;// 计算当前的ThreadLocal对象在Entry数组中的位置,其实跟hashmap一样,都是(n - 1) & hashint i = key.threadLocalHashCode & (len-1);// 只要目标位置不为空,就进行循环for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();// 期间如果遇到key相同的,则替换valueif (k == key) {e.value = value;return;}// 如果遇到key为null的,就会执行替换过期数据的逻辑if (k == null) {replaceStaleEntry(key, value, i);return;}}// 如果目标位置为空,就直接将这个Entry对象放进去tab[i] = new Entry(key, value);int sz = ++size;// 如果清理过期槽位失败并且元素数量大于等于阈值(数组长度乘2/3)就进行rehash逻辑if (!cleanSomeSlots(i, sz) && sz >= threshold)rehash();
}
4.createMap
void createMap(Thread t, T firstValue) {// 使用ThreadLocal对象和value去创建一个mapt.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {// table的初始容量为16table = new Entry[INITIAL_CAPACITY];// 使用(n - 1) & hash去找到指定的table数组的位置int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);// 给指定位置的table数组设置一个Entry对象table[i] = new Entry(firstKey, firstValue);// 设置数组大小为1size = 1;// 设置阈值setThreshold(INITIAL_CAPACITY);
}
private void setThreshold(int len) {// 阈值为16 * 2/3threshold = len * 2 / 3;
}
5.rehash
private void rehash() {// 进行探测式清理工作expungeStaleEntries();// 如果清理后 size >= threshold * 3/4 就进行扩容if (size >= threshold - threshold / 4)resize();
}
6.resize
private void resize() {// 旧的Entry数组Entry[] oldTab = table;// 旧的容量int oldLen = oldTab.length;// 新的长度为旧长度的两倍int newLen = oldLen * 2;// 创建一个新的Entry数组Entry[] newTab = new Entry[newLen];int count = 0;// 遍历旧的数组,将旧数组中的元素重新hash后放到新数组中,如果哈希冲突就往后放for (int j = 0; j < oldLen; ++j) {Entry e = oldTab[j];if (e != null) {ThreadLocal<?> k = e.get();if (k == null) {e.value = null; // Help the GC} else {int h = k.threadLocalHashCode & (newLen - 1);while (newTab[h] != null)h = nextIndex(h, newLen);newTab[h] = e;count++;}}}setThreshold(newLen);size = count;table = newTab;
}
3.ThreadLocalMap.get()详解
1.get
public T get() {// 获取当前线程Thread t = Thread.currentThread();// 获取当前线程的ThreadLocalMapThreadLocalMap map = getMap(t);// 只要map不为空if (map != null) {// 通过当前的ThreadLocal作为key去获取到对应的ThreadLocalMap.EntryThreadLocalMap.Entry e = map.getEntry(this);// 只要不为空就返回这个valueif (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}// 如果map为空就返回一个初始值nullreturn setInitialValue();
}
2.ThreadLocalMap.getEntry
private Entry getEntry(ThreadLocal<?> key) {// (n - 1) & hash 找到table数组的位置int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];// 如果key相同,直接返回if (e != null && e.get() == key)return e;else// 如果key不同,就向后找,如果最后找到了就返回,找不到就返回nullreturn getEntryAfterMiss(key, i, e);
}
3.getEntryAfterMiss
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {Entry[] tab = table;int len = tab.length;while (e != null) {ThreadLocal<?> k = e.get();if (k == key)return e;if (k == null)expungeStaleEntry(i);elsei = nextIndex(i, len);e = tab[i];}return null;}
4.ThreadLocalMap过期 key 的探测式清理流程
遍历散列数组,从开始位置向后探测清理过期数据,将过期数据的Entry设置为null。
沿途中碰到未过期的数据则将此数据rehash后重新在table数组中定位,如果定位的位置已经有了数据,则会将未过期的数据放到最靠近此位置的Entry=null的桶中,使rehash后的Entry数据距离正确的桶的位置更近一些。
5.InheritableThreadLocal原理
实现原理是子线程是通过在父线程中通过调用new Thread()方法来创建子线程,Thread#init方法在Thread的构造方法中被调用。在init方法中拷贝父线程数据到子线程中:
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {if (name == null) {throw new NullPointerException("name cannot be null");}if (inheritThreadLocals && parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);this.stackSize = stackSize;tid = nextThreadID();
}
相关文章:
ThreadLocal源码分析
文章目录 1.核心数据结构 ThreadLocalMap1.静态内部类 Entry2.真正存储数据的是table数组 2.ThreadLocal.set()方法源码详解1.set2.getMap3.ThreadLocalMap.set4.createMap5.rehash6.resize 3.ThreadLocalMap.get()详解1.get2.ThreadLocalMap.getEntry3.getEntryAfterMiss 4.Th…...
Python爬虫实战:获取笔趣阁图书信息,并做数据分析
注意:以下内容仅供技术研究,请遵守目标网站的robots.txt规定,控制请求频率避免对目标服务器造成过大压力! 1. 环境准备与反爬策略 python import requests from bs4 import BeautifulSoup import pandas as pd import re import time import random from fake_useragent …...
如何在Java EE中使用标签库?
在Java EE(现在称为Jakarta EE)中使用标签库(Tag Library),主要是通过JSP标准标签库(JSTL)或自定义标签库来实现的。标签库允许在JSP页面中使用自定义的标签,从而简化页面逻辑、增强…...
3天功能开发→3小时:通义灵码2.0+DEEPSEEK实测报告,单元测试生成准确率92%的秘密
前言 随着人工智能技术的迅猛发展,AI 赋能编程成为了必然趋势。通义灵码应运而生,它是阿里巴巴集团在人工智能与编程领域深度探索的结晶。通义灵码旨在借助 AI 的强大能力,为开发者提供更加智能、高效的编程辅助工具。通义灵码 2.0 作为其升…...
STM32 Flash详解教程文章
目录 Flash基本概念理解 Flash编程接口FPEC Flash擦除/写入流程图 Flash选项字节基本概念理解 Flash电子签名 函数读取地址下存放的数据 Flash的数据处理限制部分 编写不易,请勿搬运,感谢理解!!! Flash基本概念…...
ubuntu服务器部署
关闭欢迎消息 服务器安装好 ubuntu 系统后,进行终端登录,会显示出很多的欢迎消息 通过在用户的根目录下执行 touch .hushlogin 命令,再次登录终端就不会出现欢迎消息 修改hostname显示 修改 /etc/hostname 文件内容为主机名,保…...
小爱音箱控制手机和电视听歌的尝试
最近买了小爱音箱pro,老婆让我扔了,吃灰多年的旧音箱。当然舍不得,比小爱还贵,刚好还有一台红米手机,能插音箱,为了让音箱更加灵活,买了个2元的蓝牙接收模块Type-c供电3.5接口。这就是本次尝试起…...
问卷数据分析|SPSS实操之独立样本T检验
适用条件: 检验分类变量和定量变量之间的差异 分类变量只能为二分类变量,如性别 1.选择分析--比较平均值--独立样本检验 2. 在下方选择性别(分类变量) 3. 点击定义组,组1输入1,组2输入2 4.在上方填入定量…...
Linux 内核 IPoIB 驱动中 sysfs 属性冲突问题的分析与解决
一、引言 在 Linux 内核的设备驱动开发中,sysfs 文件系统是一种重要的机制,用于向用户空间暴露内核对象的属性信息。通过 sysfs,用户空间程序可以读取或修改设备的属性,从而实现对硬件设备的监控和配置。然而,在实际开发中,可能会遇到 sysfs 属性冲突的问题,特别是在复…...
双ESP8266-01S通讯UDP配置
第一台ESP8266(发送命令需要勾---发送新行) ATCWMODE3 ATCWSAP_DEF"CAR_wifi_Master","12345678",5,3 //设置本地wifi名称以及密码 ATCIPSTA_DEF"192.168.4.1" //设置本地IP ATCIFSR …...
【C】初阶数据结构5 -- 栈
前面学习了两种最基本的数据结构 -- 顺序表和链表,接下来就可以基于这两种数据结构来实现其他数据结构了。其实,其他的数据结构的物理结构要么是数组,要么就是链表,所以学好顺序表和链表是学好其他数据结构的基础。接下里…...
闭源大语言模型的怎么增强:提示工程 检索增强生成 智能体
闭源大语言模型的怎么增强 提示工程 检索增强生成 智能体 核心原理 提示工程:通过设计和优化提示词,引导大语言模型进行上下文学习和分解式思考,激发模型自身的思维和推理能力,使模型更好地理解和生成文本,增强其泛用性和解决问题的能力。检索增强生成:结合检索的准确…...
C语言-------结构体(1)
数据类型 (1)基本数据类型 整型 浮点型 字符型 (2)构造类型 数组 结构体 结构体: 用来处理,现实生活中,更复杂的数据的描述 用来 描述复杂数据的 一种用户自定义的数…...
org.apache.kafka.common.errors.TimeoutException
个人博客地址:org.apache.kafka.common.errors.TimeoutException | 一张假钞的真实世界 使用kafka-console-producer.sh向远端Kafka写入数据时遇到以下错误: $ bin/kafka-console-producer.sh --broker-list 172.16.72.202:9092 --topic test This is …...
Ceph集群搭建2025(squid版)
squid版本维护年限 apt install -y cephadmecho >> "deb http://mirrors.163.com/ceph/debian-squid/ bookworm main" echo >> "deb-src http://mirrors.163.com/ceph/debian-squid/ bookworm main"#安装源 cephadm install #开始初始化一个最…...
DeepSeek从入门到精通:提示词设计的系统化指南
目录 引言:AIGC时代的核心竞争力 第一部分 基础篇:提示词的本质与核心结构 1.1 什么是提示词? 1.2 提示词的黄金三角结构 第二部分 类型篇:提示词的六大范式 2.1 提示语的本质特征 2.2 提示语的类型 2.2.1 指令型提示词 …...
python后端调用Deep Seek API
python后端调用Deep Seek API 需要依次下载 ●Ollama ●Deepseek R1 LLM模型 ●嵌入模型nomic-embed-text / bge-m3 ●AnythingLLM 参考教程: Deepseek R1打造本地化RAG知识库:安装部署使用详细教程 手把手教你:deepseek R1基于 AnythingLLM API 调用本地…...
自有证书的rancher集群使用rke部署k8s集群异常
rancher使用自签域名,或者商业证书容易踩到的坑。 最开始的报错: docker logs kubelet‘s id E0214 13:04:14.590268 9614 pod_workers.go:1300] "Error syncing pod, skipping" err"failed to \"StartContainer\" for …...
【线性代数】1行列式
1. 行列式的概念 行列式的符号表示: 行列式的计算结果:一个数 计算模型1:二阶行列式 二阶行列式: 三阶行列式: n阶行列式: 🍎计算行列式 计算模型2:上三角形行列式 上三角形行列式特征:主对角线下皆为0。 上三角形行列式: 化上三角形通用方法:主对角线下,…...
DeepSeek免费部署到WPS或Office
部署到WPS - 通过OfficeAI插件接入: - 准备工作:安装最新版本的WPS Office软件;访问DeepSeek官网,点击右上角的“API开放平台”,登录账号(若无账号需先注册),登录成功后,…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...
