线程上下文-ThreadLocal原理

ThreadLocal主要作用:为每个线程提供独立的变量副本,实现线程间的数据隔离,从而避免多线程环境下的资源共享冲突。

原理

ThreadLocal有个内部类 ThreadLocalMap,顾名思义是个Map结构:key为 ThreadLocal实例,value为线程私有数据。

每个Thread线程对象内部有 ThreadLocalMap 属性,用于存储线程本地变量

public class Thread implements Runnable {


    // ...
    ThreadLocal.ThreadLocalMap threadLocals = null;
    // 下文用到
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
    
    // ...
    }

ThreadLocal每次使用都是直接调用get()、set()

ThreadLocalget()方法访问变量时,实际操作的是当前线程的ThreadLocalMap 属性。具体步骤如下:

  • 当前线程会首先查找自己的ThreadLocalMap
  • 如果找到对应的值,就返回这个值;如果没有找到,则会调用initialValue()方法来生成一个默认值,将ThreadLocalMapvalue对象返回。
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    // 当前线程ThreadLocalMap若已初始化直接返回局部变量,
    // 未初始化则初始化后返回
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

每个线程都会创建自己线程独立的ThreadLocalMap ,从而保证数据线程隔离。

内存泄漏ThreadLocalMap的键是弱引用,值不是,可能导致内存泄漏。使用后应调用remove()方法清理。

主、子线程ThreadLocal数据传递?

  1. 手动传递上下文
  2. InheritableThreadLocal可继承上下文,InheritableThreadLocal是ThreadLocal子类
  3. 线程池 TaskDecorator 装饰器,任务执行前后做一些事情(手动传递)
public class BusinessContextDecorator implements TaskDecorator {
    
    // 线程任务执行之前,把用户信息从主线程传递到子线程
    @Override
    public Runnable decorate(Runnable runnable) {
        UserContextInfo userContext = UserContext.getUserContext();
        return () -> {
            try {
                UserContext.setUserContext(userContext);
                runnable.run();
            }finally {
                UserContext.clear();
            }
        };
    }
}

@Configuration
public class ThreadPoolExecutorConfig {

    private static final int CORE_THREAD_SIZE = Runtime.getRuntime().availableProcessors() + 1;

    private static final int MAX_THREAD_SIZE = Runtime.getRuntime().availableProcessors() * 2 + 1;

    private static final int WORK_QUEUE = 1000;

    private static final int KEEP_ALIVE_SECONDS = 60;

    @Bean("taskExecutor")
    public Executor taskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(CORE_THREAD_SIZE);
        executor.setMaxPoolSize(MAX_THREAD_SIZE);
        executor.setQueueCapacity(WORK_QUEUE);
        executor.setKeepAliveSeconds(KEEP_ALIVE_SECONDS);
        executor.setThreadNamePrefix("task-thread-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        // 新增线程装饰器
        executor.setTaskDecorator(new BusinessContextDecorator());
        executor.initialize();
        return executor;
    }
}

父子数据传递

当创建一个新线程时,会调用Thread类的构造方法,在构造方法中会检查父线程的inheritableThreadLocals是否为空,如果不为空,则会将父线程的inheritableThreadLocals复制到子线程的inheritableThreadLocals中。

每个线程都有自己独立的inheritableThreadLocals实例,保证了线程间数据的隔离。

主子线程上下文隔离互不影响

public class InheritableThreadLocalExample {
    // 创建一个 InheritableThreadLocal 实例
    private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        // 在父线程中设置 InheritableThreadLocal 的值
        inheritableThreadLocal.set("Hello from parent thread");

        // 打印父线程中的值
        System.out.println("Parent thread value: " + inheritableThreadLocal.get());

        // 创建子线程
        Thread childThread = new Thread(() -> {
            // 打印子线程中继承的 InheritableThreadLocal 的值
            System.out.println("Child thread value: " + inheritableThreadLocal.get());

            // 在子线程中修改 InheritableThreadLocal 的值
            inheritableThreadLocal.set("Hello from child thread");
            System.out.println("Modified child thread value: " + inheritableThreadLocal.get());
        });

        // 启动子线程
        childThread.start();

        try {
            // 等待子线程执行完毕
            childThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 打印父线程中的值,验证父线程的值不受子线程修改的影响
        System.out.println("Parent thread value after child thread modification: " + inheritableThreadLocal.get());

        // 清除 InheritableThreadLocal 的值
        inheritableThreadLocal.remove();
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/966988.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

第31周:文献阅读

目录 摘要 Abstract 文献阅读 问题引入 研究背景 研究动机 创新点 动态预训练方法&#xff08;DynPT&#xff09; 深度循环神经网络&#xff08;DRNN&#xff09; 传感器选择 方法论 时间序列的动态预训练 异构传感器数据的DRNN 基于稀疏度的传感器过滤 实验研…

Yolo图片标注的一些问题

1.标注工具的选择 在img.net和瑞芯微的双重加持下&#xff0c;现在的计算机视觉识别已经在各行业快速推进。进行自行标注时&#xff0c;首先遇到的问题就是标注工具的选择问题&#xff0c;标注工具不需要自己手工完成——也没有必要。类似这样的通用需求&#xff0c;交给专业…

排错 -- 用React.js,Solidity,智能合约构建最新区块链应用

真枪实弹:第一个Web3项目【上集】用React.js,Solidity,智能合约构建最新区块链应用详细教程 构建web跟随b站教程中遇到了很多错误&#xff0c;从今天开始构建完整的应用&#xff0c;在此记录一些排错。 问题情况1&#xff1a;跟随视频后无Src文件 问题情况1解决方法&#xff1…

杂记:下载了BootLoader和APP到程序中无反应

杂记&#xff1a;下载了BootLoader和APP到程序中无反应 是因为采用了printf输出打印。占用了大量堆栈导致程序运行异常。并且没有打开Use MicroLIB库的话会导致无法启动程序。 解决办法&#xff1a; 1、关闭printf打印。 2、如果不关闭printf打印&#xff0c;则加大Heap_Size…

Unet 改进:引入残差模块ResidualBlock

目录 1. ResidualBlock 2. UNet 引入残差模块 Tips:融入模块后的网络经过测试,可以直接使用,设置好输入和输出的图片维度即可 1. ResidualBlock 残差连接(Residual Connection)是深度学习中一种重要的技术,主要用于解决深层网络训练中的梯度消失和网络退化问题。它首次…

对接DeepSeek

其实&#xff0c;整个对接过程很简单&#xff0c;就四步&#xff0c;获取key&#xff0c;找到接口文档&#xff0c;接口测试&#xff0c;代码对接。 获取 KEY https://platform.deepseek.com/transactions 直接付款就是了&#xff08;现在官网暂停充值2025年2月7日&#xff0…

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之上传头像和新增收货地址

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【Spring篇】【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f680;1.上传头像 -持久…

【大模型】硅基流动对接DeepSeek使用详解

目录 一、前言 二、硅基流动介绍 2.1 硅基流动平台介绍 2.1.1 平台是做什么的 2.2 主要特点与功能 2.2.1 适用场景 三、硅基流动快速使用 3.1 账户注册 3.2 token获取 3.2.1 获取token技巧 四、Cherry-Studio对接DeepSeek 4.1 获取 Cherry-Studio 4.2 Cherry-Stud…

告别2023~2024

时间过得真快&#xff0c;距离上次写作2年多了。2023年&#xff5e;2024年的这两年时光里经历太多人生大事&#xff1a; 房贷&#xff0c;提前还贷买车&#xff0c;全款拿下租房搬家媳妇怀孕&#xff0c;独自照顾&#xff0c;……老人离世开盲盒喜提千金&#xff0c;百岁宴&am…

基于yolov11的阿尔兹海默症严重程度检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv11的阿尔兹海默症严重程度检测系统是一种创新的医疗辅助工具&#xff0c;旨在通过先进的计算机视觉技术提高阿尔兹海默症的早期诊断和病情监测效率。阿尔兹海默症是一种渐进性的神经退行性疾病&#xff0c;通常表现为认知障碍、记忆丧失和语言障碍等症状…

2025我的第二次社招,写在春招之季

先说一个好消息&#xff0c;C那些事 4w star了&#xff01; 前面断更了一个月&#xff0c;本篇文章就可以看到原因&#xff0c;哈哈。 大家好&#xff0c;我叫光城&#xff0c;腾讯实习转正做后端开发&#xff0c;后去小公司做数据库内核&#xff0c;经过这几年的成长与积累&am…

使用Docker + Ollama在Ubuntu中部署deepseek

1、安装docker 这里建议用docker来部署&#xff0c;方便简单 安装教程需要自己找详细的&#xff0c;会用到跳过 如果你没有安装 Docker&#xff0c;可以按照以下步骤安装&#xff1a; sudo apt update sudo apt install apt-transport-https ca-certificates curl software-p…

Java集合List详解(带脑图)

允许重复元素&#xff0c;有序。常见的实现类有 ArrayList、LinkedList、Vector。 ArrayList ArrayList 是在 Java 编程中常用的集合类之一&#xff0c;它提供了便捷的数组操作&#xff0c;并在动态性、灵活性和性能方面取得了平衡。如果需要频繁在中间插入和删除元素&#xf…

【config目录】SpringBoot应用配置存放的地方

application.properties之前总是存放于默认的resources目录下&#xff0c;现在想想其实是不合适的&#xff0c;上线后其实这个配置是需要更改的&#xff0c;因此我们不要打包到jar包中。 根据SpringBoot应用配置查找规则&#xff0c;如果在jar包中resources目录查找不到的话&a…

洛谷 B3616 【模板】队列

B3616 【模板】队列 - 洛谷 | 计算机科学教育新生态 题目描述 请你实现一个队列&#xff08;queue&#xff09;&#xff0c;支持如下操作&#xff1a; push(x)&#xff1a;向队列中加入一个数 x。pop()&#xff1a;将队首弹出。如果此时队列为空&#xff0c;则不进行弹出操作…

布丁扫描:手机扫描的纯净之选

在众多手机扫描软件中&#xff0c;布丁扫描凭借其纯粹的使用体验脱颖而出。它是一款功能强大且完全免费的手机端扫描工具&#xff0c;没有任何广告干扰&#xff0c;用户可以畅享纯净的使用过程。更值得一提的是&#xff0c;布丁扫描从未设置过充值入口&#xff0c;也不会在扫描…

2025 CCF BDCI|“基于TPU平台的OCR模型性能优化”一等奖作品

2024年12月&#xff0c;中国计算机学会在海南博鳌成功举办了第十二届CCF大数据与计算智能大赛&#xff08;简称2024 CCF BDCI&#xff09;。本届比赛的算能赛道吸引了1748名选手报名&#xff0c;经过激烈角逐&#xff0c;北京航空航天大学的“常务副SOTA”团队脱颖而出&#xf…

22.[前端开发]Day22-CSS单位-CSS预处理器-移动端视口

1 CSS常见单位详解 CSS中的单位 CSS中的绝对单位&#xff08; Absolute length units &#xff09; CSS中的相对单位&#xff08; Relative length units &#xff09; 1.em: 相对自己的font-size&#xff1b;如果自己没有设置, 那么会继承父元素的font-size 2.如果font-size中…

网站改HTTPS方法

默认的网站建设好后打开的样子那看起来像是钓鱼网站&#xff0c;现在的浏览器特别只能&#xff0c;就是你新买来的电脑默认的浏览器同样也会出现这样“不安全”提示。 传输协议启动了向全球用户安全传输网页内容的流程。然而&#xff0c;随着HTTPS的推出&#xff0c;传输协议通…

MySQL的底层原理与架构

前言 了解MySQL的架构和原理对于很多的后续很多的操作会有很大的帮助与理解。并且很多知识都与底层架构相关联。 了解MySQL架构 通过上面的架构图可以得知&#xff0c;Server层中主要由 连接器、查询缓存、解析器/分析器、优化器、执行器 几部分组成的&#xff0c;下面将主要…