当前位置: 首页 > 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;…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

什么是库存周转?如何用进销存系统提高库存周转率?

你可能听说过这样一句话&#xff1a; “利润不是赚出来的&#xff0c;是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业&#xff0c;很多企业看着销售不错&#xff0c;账上却没钱、利润也不见了&#xff0c;一翻库存才发现&#xff1a; 一堆卖不动的旧货…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解

本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

Python Ovito统计金刚石结构数量

大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...

Bean 作用域有哪些?如何答出技术深度?

导语&#xff1a; Spring 面试绕不开 Bean 的作用域问题&#xff0c;这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开&#xff0c;结合典型面试题及实战场景&#xff0c;帮你厘清重点&#xff0c;打破模板式回答&#xff0c…...

如何通过git命令查看项目连接的仓库地址?

要通过 Git 命令查看项目连接的仓库地址&#xff0c;您可以使用以下几种方法&#xff1a; 1. 查看所有远程仓库地址 使用 git remote -v 命令&#xff0c;它会显示项目中配置的所有远程仓库及其对应的 URL&#xff1a; git remote -v输出示例&#xff1a; origin https://…...

Qt Quick模块功能及架构

Qt 6.0 中的 Qt Quick 模块是构建现代、动态用户界面的核心框架&#xff0c;基于声明式编程&#xff08;QML&#xff09;和 JavaScript&#xff0c;专注于高性能、流畅的动画和跨平台 UI 开发。、 一、主要功能改进 1. Qt Quick 核心架构 QML 引擎升级&#xff1a;Qt 6.0 使用…...

Codeforces Educational 179(ABCDE)

前言 byd这组题纯靠感觉是吧…^_^ b题赛时举了无数个例子都没想明白&#xff0c;然后一直卡到结束&#xff0c;后面题都没看到&#xff0c;结果补题的时候c题d题直接秒了…-_-|| A. Energy Crystals #include <bits/stdc.h> using namespace std;typedef long long …...