当前位置: 首页 > news >正文

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数组

CleanShot 2025-02-02 at 11.57.50@2x

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;}

CleanShot 2025-02-02 at 12.10.06@2x

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&#xff08;现在称为Jakarta EE&#xff09;中使用标签库&#xff08;Tag Library&#xff09;&#xff0c;主要是通过JSP标准标签库&#xff08;JSTL&#xff09;或自定义标签库来实现的。标签库允许在JSP页面中使用自定义的标签&#xff0c;从而简化页面逻辑、增强…...

3天功能开发→3小时:通义灵码2.0+DEEPSEEK实测报告,单元测试生成准确率92%的秘密

前言 随着人工智能技术的迅猛发展&#xff0c;AI 赋能编程成为了必然趋势。通义灵码应运而生&#xff0c;它是阿里巴巴集团在人工智能与编程领域深度探索的结晶。通义灵码旨在借助 AI 的强大能力&#xff0c;为开发者提供更加智能、高效的编程辅助工具。通义灵码 2.0 作为其升…...

STM32 Flash详解教程文章

目录 Flash基本概念理解 Flash编程接口FPEC Flash擦除/写入流程图 Flash选项字节基本概念理解 Flash电子签名 函数读取地址下存放的数据 Flash的数据处理限制部分 编写不易&#xff0c;请勿搬运&#xff0c;感谢理解&#xff01;&#xff01;&#xff01; Flash基本概念…...

ubuntu服务器部署

关闭欢迎消息 服务器安装好 ubuntu 系统后&#xff0c;进行终端登录&#xff0c;会显示出很多的欢迎消息 通过在用户的根目录下执行 touch .hushlogin 命令&#xff0c;再次登录终端就不会出现欢迎消息 修改hostname显示 修改 /etc/hostname 文件内容为主机名&#xff0c;保…...

小爱音箱控制手机和电视听歌的尝试

最近买了小爱音箱pro&#xff0c;老婆让我扔了&#xff0c;吃灰多年的旧音箱。当然舍不得&#xff0c;比小爱还贵&#xff0c;刚好还有一台红米手机&#xff0c;能插音箱&#xff0c;为了让音箱更加灵活&#xff0c;买了个2元的蓝牙接收模块Type-c供电3.5接口。这就是本次尝试起…...

问卷数据分析|SPSS实操之独立样本T检验

适用条件&#xff1a; 检验分类变量和定量变量之间的差异 分类变量只能为二分类变量&#xff0c;如性别 1.选择分析--比较平均值--独立样本检验 2. 在下方选择性别&#xff08;分类变量&#xff09; 3. 点击定义组&#xff0c;组1输入1&#xff0c;组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 -- 栈

前面学习了两种最基本的数据结构 -- 顺序表和链表&#xff0c;接下来就可以基于这两种数据结构来实现其他数据结构了。其实&#xff0c;其他的数据结构的物理结构要么是数组&#xff0c;要么就是链表&#xff0c;所以学好顺序表和链表是学好其他数据结构的基础。接下里&#xf…...

闭源大语言模型的怎么增强:提示工程 检索增强生成 智能体

闭源大语言模型的怎么增强 提示工程 检索增强生成 智能体 核心原理 提示工程:通过设计和优化提示词,引导大语言模型进行上下文学习和分解式思考,激发模型自身的思维和推理能力,使模型更好地理解和生成文本,增强其泛用性和解决问题的能力。检索增强生成:结合检索的准确…...

C语言-------结构体(1)

数据类型 &#xff08;1&#xff09;基本数据类型 整型 浮点型 字符型 &#xff08;2&#xff09;构造类型 数组 结构体 结构体: 用来处理&#xff0c;现实生活中&#xff0c;更复杂的数据的描述 用来 描述复杂数据的 一种用户自定义的数…...

org.apache.kafka.common.errors.TimeoutException

个人博客地址&#xff1a;org.apache.kafka.common.errors.TimeoutException | 一张假钞的真实世界 使用kafka-console-producer.sh向远端Kafka写入数据时遇到以下错误&#xff1a; $ 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从入门到精通:提示词设计的系统化指南

目录 引言&#xff1a;AIGC时代的核心竞争力 第一部分 基础篇&#xff1a;提示词的本质与核心结构 1.1 什么是提示词&#xff1f; 1.2 提示词的黄金三角结构 第二部分 类型篇&#xff1a;提示词的六大范式 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 参考教程&#xff1a; Deepseek R1打造本地化RAG知识库:安装部署使用详细教程 手把手教你&#xff1a;deepseek R1基于 AnythingLLM API 调用本地…...

自有证书的rancher集群使用rke部署k8s集群异常

rancher使用自签域名&#xff0c;或者商业证书容易踩到的坑。 最开始的报错&#xff1a; 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插件接入&#xff1a; - 准备工作&#xff1a;安装最新版本的WPS Office软件&#xff1b;访问DeepSeek官网&#xff0c;点击右上角的“API开放平台”&#xff0c;登录账号&#xff08;若无账号需先注册&#xff09;&#xff0c;登录成功后&#xff0c;…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

数据库分批入库

今天在工作中&#xff0c;遇到一个问题&#xff0c;就是分批查询的时候&#xff0c;由于批次过大导致出现了一些问题&#xff0c;一下是问题描述和解决方案&#xff1a; 示例&#xff1a; // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全&#xff08;Thread Safety&#xff09; 线程安全是指在多线程环境下&#xff0c;某个函数、类或代码片段能够被多个线程同时调用时&#xff0c;仍能保证数据的一致性和逻辑的正确性&#xf…...

HDFS分布式存储 zookeeper

hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架&#xff0c;允许使用简单的变成模型跨计算机对大型集群进行分布式处理&#xff08;1.海量的数据存储 2.海量数据的计算&#xff09;Hadoop核心组件 hdfs&#xff08;分布式文件存储系统&#xff09;&a…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

DingDing机器人群消息推送

文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人&#xff0c;点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置&#xff0c;详见说明文档 成功后&#xff0c;记录Webhook 2 API文档说明 点击设置说明 查看自…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...