InheritableThreadLocal和ThreadLocal的区别和使用场景

快人快语,先说结论,InheritableThreadLocal 是 ThreadLocal 的一个子类,它包含ThreadLocal 的所有功能并且扩展了 ThreadLocal 的功能,允许父线程中的 InheritableThreadLocal 变量的值被子线程继承。这意味着,当创建一个新的线程时,这个新线程可以访问其父线程中 InheritableThreadLocal 变量的值。

先说ThreadLocal

ThreadLocal 是 Java 中的一个类,它提供了一种线程局部(thread-local)变量。这些变量与普通的变量不同,因为每一个访问变量的线程都有其自己的独立初始化的变量副本。ThreadLocal 变量通常被私有 static 字段包含,它们被声明为 ThreadLocal 类型。

以下是 ThreadLocal 的主要特点和作用:

  1. 线程隔离ThreadLocal 为每个线程提供了它自己的变量副本。因此,一个线程不能访问或修改另一个线程的 ThreadLocal 变量。这有助于解决多线程环境下的数据共享和同步问题。
  2. 减少线程间同步:由于每个线程都有自己的 ThreadLocal 变量副本,因此不需要使用同步机制(如 synchronized)来避免并发访问问题。这可以提高多线程程序的性能。
  3. 方便管理线程上下文信息ThreadLocal 可以用来存储和访问线程特定的上下文信息,如用户ID、事务ID等。这样,在程序的不同部分,都可以方便地访问这些线程特定的信息。
  4. 内存泄漏问题:需要注意的是,ThreadLocal 可能会导致内存泄漏。当一个线程结束时,它使用的所有 ThreadLocal 实例通常不会被垃圾收集器回收,除非显式地删除这些实例。因此,在使用 ThreadLocal 时,需要确保在线程结束时正确地清理相关资源。

以下是一个简单的 ThreadLocal 示例:

public class ThreadLocalExample {// 创建一个 ThreadLocal 变量private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 线程1Thread thread1 = new Thread(() -> {threadLocal.set("Thread 1 data"); // 设置线程1的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程1的 ThreadLocal 变量});// 线程2Thread thread2 = new Thread(() -> {threadLocal.set("Thread 2 data"); // 设置线程2的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程2的 ThreadLocal 变量});thread1.start();thread2.start();}
}

在这个示例中,我们创建了一个 ThreadLocal<String> 变量,并在两个线程中分别设置了不同的值。每个线程都只能访问和修改它自己的 ThreadLocal 变量副本,因此输出将会是:

Thread-0: Thread 1 data
Thread-1: Thread 2 data

这显示了 ThreadLocal 如何为每个线程提供其自己的变量副本。
注意,上文案例是一个错误的用法,theadLocal变量一定要记得用后即焚,否则会有内存泄漏风险。当线程不再需要时,应该显式地调用ThreadLocal实例的remove()方法,以从当前线程的ThreadLocalMap中移除对应的条目。这样可以确保即使线程对象被回收,其关联的ThreadLocal变量也不会继续占用内存。

我很想从源码的角度讲一讲ThreadLocal的故事,比如ThreadLocal是怎么做到线程隔离的,ThreadLocal和ThreadLocalMap的关系等等,这些东西其实翻一翻ThreadLocal的源码就很快有答案,建议从它的set方法开始看起。本文不多做介绍。

InheritableThreadLocal

简单来说,InheritableThreadLocal就是可以继承给子线程的ThreadLocal,ThreadLocal本身并不能继承给子线程,示例如下:

    public static void main(String[] args) {//// 线程1Thread thread1 = new Thread(() -> {threadLocal.set("Thread 1 data"); // 设置线程1的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程1的 ThreadLocal 变量// 创建子线程  Thread childThread = new Thread(() -> {System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 子线程尝试获取并打印父线程设置的ThreadLocal 变量的值  });childThread.setName("Child Thread");childThread.start();});// 线程2Thread thread2 = new Thread(() -> {threadLocal.set("Thread 2 data"); // 设置线程2的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程2的 ThreadLocal 变量});thread1.start();thread2.start();}

运行结果:

Thread-0: Thread 1 data
Thread-1: Thread 2 data
Child Thread: null

这个案例说明ThreadLocal并不能继承给子线程,那么换成InheritableThreadLocal会如何:

    private static final ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();public static void main(String[] args) {// 线程1Thread thread1 = new Thread(() -> {threadLocal.set("Thread 1 data"); // 设置线程1的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程1的 ThreadLocal 变量// 创建子线程Thread childThread = new Thread(() -> {System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 子线程获取并打印父线程设置的 InheritableThreadLocal 变量的值});childThread.setName("Child Thread");childThread.start();});// 线程2Thread thread2 = new Thread(() -> {threadLocal.set("Thread 2 data"); // 设置线程2的 ThreadLocal 变量System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get()); // 获取并打印线程2的 ThreadLocal 变量});thread1.start();thread2.start();}

上面的代码案例仅仅改了一处,就是将threadLocal的对象类型改为了InheritableThreadLocal,其余不变,运行结果如下:

Thread-1: Thread 2 data
Thread-0: Thread 1 data
Child Thread: Thread 1 data

运行结果表示Child Thread成功拿到了父线程的ThreadLocal变量值,这是怎么做到ThreadLocal传递的?翻开Thread类的构造方法我们一目了然:

 private Thread(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {// 获取父线程变量Thread parent = currentThread();// 如果父线程的inheritableThreadLocals有值,则传递给当前线程if (inheritThreadLocals && parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);}

引申思考

本文的重点来了,如上面源码所示,InheritableThreadLocal是在Thread类的构造方法里面完成值传递的,如果是主线程不是使用new Thread()的方式而是使用了线程池,这时InheritableThreadLocal能够继承给子线程吗?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常用芯片学习——TP4057电源管理芯片

TP40578 500mA线性锂离子电池充电器 芯片介绍 TP4057是一款性能优异的单节锂离子电池恒流/恒压线性充电器。TP4057采用S0T23-6封装配合较少的外围原件使其非常适用于便携式产品&#xff0c;并且适合给USB电源以及适配器电源供电。 基于特殊的内部MOSFET架构以及防倒充电路&a…

Python实现一笔画游戏

Python实现一笔画游戏 关于一笔画介绍可参见“HTML5实现一笔画游戏”https://blog.csdn.net/cnds123/article/details/136669088 在Python中&#xff0c;Tkinter是一个广泛使用的标准GUI库&#xff0c;我们将使用它来实现这个游戏。 先给出效果图&#xff1a; 连接线段时&am…

MQL语言实现抽象工厂模式

文章目录 一、定义抽象产品接口二、定义抽象工厂接口三、定义具体产品四、定义具体工厂五、定义工厂客户端六、客户端调用工厂客户端七、抽象工厂模式的结构 一、定义抽象产品接口 //------------------------------------------------------------------ //| participants …

城乡居民基本医疗信息管理系统|基于Springboot的城乡居民基本医疗信息管理系统设计与实现(源码+数据库+文档)

城乡居民基本医疗信息管理系统目录 目录 基于Springboot的城乡居民基本医疗信息管理系统设计与实现 一、前言 二、系统设计 三、系统功能设计 1、病例管理 2、医院资讯信息管理 3、医院资讯类型管理 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选…

微信小程序开发学习笔记《21》uni-app框架-楼层图片跳转

微信小程序开发学习笔记《21》uni-app框架-楼层图片跳转 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读uni-app对应官方文档 一、创建新的分包goods_list 二、将请求到的楼层数据url调整为本地的 可以看到上图是请求…

关于固件的简单解释

我不喜欢等人也不喜欢被别人等——赤砂之蝎 简而言之 固件是什么 固件&#xff08;Firmware&#xff09;是一种软件类型&#xff0c;它是嵌入式系统中的一部分&#xff0c;通常存储在设备的非易失性存储器中&#xff0c;如闪存或ROM&#xff08;只读存储器&#xff09;。与操作…

蓝桥杯---棋盘(典型的二维差分问题)

题目链接&#xff1a;棋盘 这道题真的是非常典型的二维差分问题了&#xff08;在我个人看来&#xff09;&#xff0c;题目中的0和1&#xff0c;我们直接让差分数组&#xff0c;偶数就是0&#xff0c;奇数就是1.初始化是0&#xff0c;是白子&#xff08;偶数&#xff09;&#x…

libevent中bufferevent事件及常用的API函数

自带buffer的事件-bufferevent bufferevent实际上也是一个event&#xff0c;只不过比普通的event高级&#xff0c;他的内部有两个缓冲区&#xff0c;以及一个文件描述符&#xff08;网络套接字&#xff09;。一个网络套接字有读写两个缓冲区&#xff0c;bufferevent同样也带有…

探索仿函数(Functor):C++中的灵活函数对象

文章目录 一、仿函数定义及使用二、仿函数与函数指针的区别三、仿函数与算法的关系四、仿函数的实践用例 在C编程中&#xff0c;我们经常需要对数据进行排序、筛选或者其他操作。为了实现这些功能&#xff0c;C标准库提供了许多通用的算法和容器&#xff0c;而其中一个重要的概…

思科防火墙如何配置静态NAT

环境&#xff1a; 思科防火墙ASA5555 Cisco Adaptive Security Appliance Software Version 9.4(2)6 Device Manager Version 7.5(2)153 问题描述&#xff1a; 思科防火墙如何配置静态NAT 解决方案&#xff1a; 1.做之前要先查一下有没有端口被占用&#xff0c;要和业务确…

nut-ui组件库icon中使用阿里图标

1.需求 基本每个移动端组件库都有组件 icon组件 图标组件、 但是很多组件库中并找不到我们需要的图标 这时候 大家有可能会找图标库 最大众的就是iconfont的图标了 2.使用 有很多方式去使用这个东西 比如将再限链接中的css引入 在使用 直接下载图标 symbol 方式 等....…

【NR 定位】3GPP NR Positioning 5G定位标准解读(十三)-DL-AoD定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

buuctf warmup 超详细

目录 1.代码审计&#xff1a; 2.逻辑分析 3.总结分析 4.分析记录 5.疑点解答 1.代码审计&#xff1a; <?phphighlight_file(__FILE__);class emmm //定义了一个类{public static function checkFile(&$page) 类里面又申明创建…

移动通信网络AT指令

PLMN 移动通信网络PLMN = MCC + MNC,PLMN由MCC移动国家码和MNC移动网络码组成,例如:中国移动GSM的PLMN为:46000(MCC:460, MNC:00)中国联通GSM的PLMN国家码MCC为460,网络码MNC为01: 46001中国大陆相关的移动网络码:中国移动系统使用00、02、04、07,中国联通GSM系统…

题目 2656: 蓝桥杯2022年第十三届省赛真题-刷题统计

时间限制: 3s 内存限制: 320MB 提交: 31968 解决: 5255 题目描述 小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 a 道题目&#xff0c;周六和周日每天做 b 道题目。请你帮小明计算&#xff0c;按照计划他将在第几天实现做题数大于等于 n 题&#xff1…

3、Redis持久化之RDB

Redis持久化之RDB 何为持久化&#xff1f;所谓持久化就是将数据持久化保存&#xff0c;也就是将数据保存到硬盘中。 Redis持久化的方法&#xff1a; RDB AOF AOF在下一篇介绍 什么是RDB RDB是redis默认的持久化策略&#xff0c;在RDB模式下&#xff0c;可以将redis在内存…

Android 架构师研发技术进阶之路:不同阶段需要掌握的那些技术及软技能

资深 而到了资深层次&#xff0c;技术栈已经不再是阻碍。能够从更高层面看待问题&#xff0c;理解整个系统的设计&#xff0c;作为系统架构师的角色存在。 1. 理解微服务、SOA思想&#xff0c;对于后端开发有一定涉猎。 2. 了解前端研发工具和思想&#xff0c;知道vue react…

centos破解root密码以及如何防止他人破解root密码

目录 破解root密码 服务器重启 1.再重启页面上下选择第一个按e进入内核编辑模式 2.找到linux16开头的一行&#xff0c;光标移动到最后添加 init/bin/sh Ctrlx 保存 3.进入单用户模式 4.重新挂在根分区 5.关闭selinux 6.更新密码 passwd 7.在根分区下面创建一个隐藏文件…

【C语言步行梯】一维数组、二维数组介绍与应用详谈

&#x1f3af;每日努力一点点&#xff0c;技术进步看得见 &#x1f3e0;专栏介绍&#xff1a;【C语言步行梯】专栏用于介绍C语言相关内容&#xff0c;每篇文章将通过图片代码片段网络相关题目的方式编写&#xff0c;欢迎订阅~~ 文章目录 为什么要有数组&#xff1f;一维数组数组…

uni-app微信小程序上拉加载,下拉刷新

pages.json配置官网链接 onPullDownRefresh、onReachBottom函数跟生命周期同级 data() {return {orderList:[],total: null, //总共多少条数据page: 1,pageSize: 10,} }, onLoad() {}, mounted(){this.getInfo() }, methods:{getInfo(){API.getListxxx().then(res > {const…