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

【多线程】ThreadLocal是什么?有哪些使用场景?使用ThreadLocal需要注意些什么?

文章目录

  • 前言
  • 一、ThreadLocal 是什么?
  • 二、有哪些使用场景?
  • 三、实现原理
  • 四、在线程池中使用 ThreadLocal 为什么可能导致内存泄露呢?
  • 五、线程池中,如何正确使用 ThreadLocal?
  • 六、ThreadLocal 核心方法

前言

一、ThreadLocal 是什么?

ThreadLocal 是线程本地存储,在每个线程中都创建了一个 ThreadLocalMap 对象,每个线程可以访问自己内部 ThreadLocalMap 对象内的 value。

二、有哪些使用场景?

经典的使用场景是为每个线程分配一个 JDBC 连接 Connection。这样就可以保证每个线程的都在各自的 Connection 上进行数据库的操作,不会出现 A 线程关了 B线程正在使用的 Connection; 还有 Session管理等问题。

ThreadLocal 使用例子:

public class TestThreadLocal {//线程本地存储变量private static final ThreadLocal<Integer> THREAD_LOCAL_NUM = new ThreadLocal<Integer>() {@Overrideprotected Integer initialValue() {return 0;}};public static void main(String[] args) {for (int i = 0; i < 3; i++) {//启动三个线程Thread t = new Thread() {@Overridepublic void run() {add10ByThreadLocal();}};t.start();}}/*** 线程本地存储变量加 5*/private static void add10ByThreadLocal() {for (int i = 0; i < 5; i++) {Integer n = THREAD_LOCAL_NUM.get();n += 1;THREAD_LOCAL_NUM.set(n);System.out.println(Thread.currentThread().getName() + " : ThreadLocal num=" + n);}}}

打印结果:启动了 3 个线程,每个线程最后都打印到 “ThreadLocal num=5”,而不是 num 一直在累加直到值等于 15

Thread-0 : ThreadLocal num=1
Thread-1 : ThreadLocal num=1
Thread-0 : ThreadLocal num=2
Thread-0 : ThreadLocal num=3
Thread-1 : ThreadLocal num=2
Thread-2 : ThreadLocal num=1
Thread-0 : ThreadLocal num=4
Thread-2 : ThreadLocal num=2
Thread-1 : ThreadLocal num=3
Thread-1 : ThreadLocal num=4
Thread-2 : ThreadLocal num=3
Thread-0 : ThreadLocal num=5
Thread-2 : ThreadLocal num=4
Thread-2 : ThreadLocal num=5
Thread-1 : ThreadLocal num=5

三、实现原理

按照我们第一直觉,感觉 ThreadLocal 内部肯定是有个 Map 结构,key 存了 Thread,value 存了 本地变量 V 的值。每次通过 ThreadLocal 对象的 get() 和 set(T value) 方法获取当前线程里存的本地变量、设置当前线程里的本地变量。

在这里插入图片描述

而 JDK 的实现里面这个 Map 是属于 Thread,而非属于 ThreadLocal。ThreadLocal 仅是一个代理工具类,内部并不持有任何与线程相关的数据,所有和线程相关的数据都存储在 Thread 里面。ThreadLocalMap 属于 Thread 也更加合理。

还有一个更加深层次的原因,这样设计不容易产生内存泄露。
ThreadLocal 持有的 Map 会持有 Thread 对象的引用,只要 ThreadLocal 对象存在,那么 Map 中的 Thread 对象就永远不会被回收。ThreadLocal 的生命周期往往比线程要长,所以这种设计方案很容易导致内存泄露。

JDK 的实现中 Thread 持有 ThreadLocalMap,而且 ThreadLocalMap 里对 ThreadLocal 的引用还是弱引用(WeakReference),所以只要 Thread 对象可以被回收,那么 ThreadLocalMap 就能被回收。JDK 的这种实现方案复杂但更安全。

在这里插入图片描述

四、在线程池中使用 ThreadLocal 为什么可能导致内存泄露呢?

在线程池中线程的存活时间太长,往往都是和程序同生共死的,这样 Thread 持有的 ThreadLocalMap 一直都不会被回收,再加上 ThreadLocalMap 中的 Entry 对 ThreadLocal 是弱引用(WeakReference),所以只要 ThreadLocal 结束了自己的生命周期是可以被回收掉的。
Entry 中的 Value 是被 Entry 强引用的,即便 value 的生命周期结束了,value 也是无法被回收的,导致内存泄露。

五、线程池中,如何正确使用 ThreadLocal?

在 finally 代码块中手动清理 ThreadLocal 中的 value,调用 ThreadLocal 的 remove()方法。

六、ThreadLocal 核心方法

  • 设置 Thread 对应的 Value 值,首次会创建一个 ThreadLocalMap ,添加 ThreadLocal - Value 到 ThreadLocalMap 中,并且绑定 ThreadLocalMap 到当前线程。
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);
}
  • 创建 ThreadLocalMap,绑定到当前线程。
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
  • 通过 ThreadLocalMap 获取当前线程的存储的 Value 值
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();
}
  • 设置 ThreadLocal 的初始化值,未 set(T value) 初次获取 Thread 对应的 Value 值时会调用,即被 setInitialValue 方法调用。需要重写该方法。
protected T initialValue() {return null;
}
  • 移除当前线程存储的 Value 值。当 ThreadLocal 不在使用,最好在 finally 语句块中,调用 remove() 方法,释放去 Value 的引用,避免内存泄露。
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);
}

相关文章:

【多线程】ThreadLocal是什么?有哪些使用场景?使用ThreadLocal需要注意些什么?

文章目录 前言一、ThreadLocal 是什么&#xff1f;二、有哪些使用场景&#xff1f;三、实现原理四、在线程池中使用 ThreadLocal 为什么可能导致内存泄露呢&#xff1f;五、线程池中&#xff0c;如何正确使用 ThreadLocal&#xff1f;六、ThreadLocal 核心方法 前言 一、Threa…...

一种基于动态代理的通用研发提效解决方案

作为一名研发人员&#xff0c;除了业务开发之外&#xff0c;研发提效是一个永恒的话题&#xff0c;而女娲正是这一话题下进行的一次全面的剖析和实践。 作者&#xff1a;张全洪(钝悟) 一、女娲是什么 女娲是业务研发同学&#xff08;开发、测试、运维&#xff09;在软件迭代的…...

【vue3】一些关于hooks的使用经验

前言 最近接到了一个需求&#xff0c;隔壁嵌入式部门希望我们用前端解析渲染Kconfig表单。这篇文章用来记录一下本次使用hook pinia vue3的经验 hooks hooks的概念最早是在 React 中听到的&#xff0c;虽然早些时间也写过一点react&#xff0c;但也只是照葫芦画瓢&#xf…...

面试系列 - Java 并发容器详解

Java 并发容器是一组用于在多线程环境下安全访问和操作数据的数据结构。它们提供了线程安全的集合和映射&#xff0c;使开发者能够更轻松地处理并发编程问题。 一、Java并发容器 ConcurrentHashMap&#xff1a; 它是一个高效的并发哈希表&#xff0c;支持多线程并发操作而不需…...

使用动态住宅代理还能带来哪些好处?

一、什么是动态住宅代理ip 动态住宅代理是一种代理技术&#xff0c;它利用代理服务器中转用户和目标服务器之间的网络流量&#xff0c;实现用户真实位置的屏蔽。代理提供商会有自己的ip大池子&#xff0c;当你通过代理服务器向网站发送请求时&#xff0c;服务器会从池子中选中…...

笙默考试管理系统-MyExamTest----codemirror(18)

笙默考试管理系统-MyExamTest----codemirror&#xff08;18&#xff09; 目录 一、 笙默考试管理系统-MyExamTest----codemirror 二、 笙默考试管理系统-MyExamTest----codemirror 三、 笙默考试管理系统-MyExamTest----codemirror 四、 笙默考试管理系统-MyExamTest---…...

TGA格式文件转材质

今天淘宝上买了一个美女的模型&#xff0c;是blender的源文件&#xff0c;上面说有fbx格式的。我用unity&#xff0c;所以觉得应该可以用。文件内容如下图&#xff1a; FBX文件夹打开后&#xff0c;内容如下图所示&#xff0c;当时就预感到可能没有色彩。 unity打开后果然发现只…...

IP应用场景查询API:深入了解网络用户行为的利器

前言 随着数字时代的不断发展&#xff0c;互联网已经成为人们生活的重要组成部分。而随着越来越多的业务和社交活动迁移到在线平台上&#xff0c;了解和理解网络用户行为变得至关重要。为了满足这个需求&#xff0c;IP 应用场景查询 API 崭露头角&#xff0c;成为深入了解网络…...

docker从零部署jenkins保姆级教程(上)

jenkins&#xff0c;基本是最常用的持续集成工具。在实际的工作中&#xff0c;后端研发一般没有jenkins的操作权限&#xff0c;只有一些查看权限&#xff0c;但是我们的代码是经过这个工具构建出来部署到服务器的&#xff0c;所以我觉着有必要了解一下这个工具的搭建过程以及简…...

2023数模A题——定日镜场的优化问题

A题——定日镜场的优化问题 思路&#xff1a;该题主要考察的几何知识和天文学知识&#xff0c;需要不同角度下的镜面和遮挡情况。 资料获取 问题1&#xff1a; 若将吸收塔建于该圆形定日镜场中心&#xff0c;定日镜尺寸均为 6 m6 m&#xff0c;安装高度均为 4 m&#xff0c;且…...

Container is running beyond memory limits

问题 Hadoop环境中&#xff0c;执行MapReduce程序或者Hive 任务时候&#xff0c;任务执行失败&#xff0c;提示内存不足。 Container is running 337869312B beyond the VIRTUAL’ memory limit.Current usage:295.8 NB of 1 GB physical memoryused;2.4 GB of 2.1 GB virtual…...

Java后端开发面试题——JVM虚拟机篇

目录 什么是程序计数器&#xff1f; 你能给我详细的介绍Java堆吗? 什么是虚拟机栈 1. 垃圾回收是否涉及栈内存&#xff1f; 2. 栈内存分配越大越好吗&#xff1f; 3. 方法内的局部变量是否线程安全&#xff1f; 4.什么情况下会导致栈内存溢出&#xff1f; 5.堆栈的区别…...

SpringMVC增删改查(CRUD)的实现

目录 前言 一、前期准备 1.pom.xml---依赖与插件的导入 2.jdbc.properties---数据库连接 3.log4j2.xml---日志文件 4.spring-mybatis 5.spring-context 6.spring-mvc 二、增删改查的实现 1.model与mapper层的生成 2.biz层 3.工具类 4.controller层 三、测试结果 总…...

智安网络|面临日益增长的安全威胁:云安全和零信任架构的重要性

随着云计算技术的快速发展和广泛应用&#xff0c;云安全和零信任架构变得愈发重要。在数字化时代&#xff0c;云计算技术得到了广泛的应用和推广。企业和组织借助云服务提供商的强大能力&#xff0c;实现了高效、灵活和可扩展的IT基础设施。然而&#xff0c;随着云环境的快速发…...

JVM常用调优策略

1、JVM调优的核心关注指标 调优之前首先我们要知道怎样才算是“优”&#xff0c;不能笼统的说我的程序性能很好&#xff0c;所以就需要有一个具体的指标来衡量性能情况&#xff0c;而在JVM里面衡量性能两个指标分别“吞吐量”和“停顿时间”。 吞吐量&#xff1a;程序运行过程…...

自动化防火墙放行目标域名IP

#!/bin/bash # 设置要获取IP地址的域名 domain"yourdomain.com"# 获取域名的IP地址 new_ip$(dig short A $domain)# 移除之前添加放行的IP地址&#xff08;通过备注找它的编号&#xff09; rule_number$(iptables -L INPUT -n --line-numbers -v | awk -v domain&quo…...

12.2RAC环境从RAC转为单机模式的问题处理

近期&#xff0c;在某用户的测试环境&#xff0c;需要将RAC转为单机模式&#xff0c;然后进行数据恢复&#xff1b;一开始只是将数据库软件通过make -f ins_rdbms.mk rac_off 和 make -f ins_rdbms.mk ioracle关闭RAC模式&#xff1b;然后在启动数据库&#xff08;sqlplus / a…...

Docker 中 jdk8容器里无法使用 JDK 的 jmap 等命令的问题

一、问题描述 项目部署在 CentOS 服务器上。项目偶尔会出现无响应的情况&#xff0c;这时理所当然要上去用 JDK 相关命令看看堆栈和GC等信息了。 进入 Java 程序所在容器&#xff1a;docekr-compose exec api bash&#xff0c;进入到 api 容器的 bash 终端。 jps 打印 Java …...

typeScript--[es6class类实现继承]

一.js中实现继承 // js实现继承 // 父类 function Father(name) {this.name namethis.say function () {console.log(this.name "在唱歌")} } var f new Father("逍遥的码农")// 子类 function Son(name) {Father.call(this, name) } Son.prototype …...

解决eclipse的报错:Must declare a named package because this compilation

刚安装完成eclipse&#xff0c; 创建类的时候报错 报错信息如下&#xff1a; 原因&#xff1a;新版本的ECLIPSE要求每一个类都必须定义在包里面 解决方法&#xff1a;创建类的时候指定类的名字&#xff0c;如下图&#xff1a;Package 里面填写ch3&#xff0c;表示包名 创建完成…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)

要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况&#xff0c;可以通过以下几种方式模拟或触发&#xff1a; 1. 增加CPU负载 运行大量计算密集型任务&#xff0c;例如&#xff1a; 使用多线程循环执行复杂计算&#xff08;如数学运算、加密解密等&#xff09;。运行图…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)

本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...

Python Ovito统计金刚石结构数量

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

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...

智能职业发展系统:AI驱动的职业规划平台技术解析

智能职业发展系统&#xff1a;AI驱动的职业规划平台技术解析 引言&#xff1a;数字时代的职业革命 在当今瞬息万变的就业市场中&#xff0c;传统的职业规划方法已无法满足个人和企业的需求。据统计&#xff0c;全球每年有超过2亿人面临职业转型困境&#xff0c;而企业也因此遭…...

ArcPy扩展模块的使用(3)

管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如&#xff0c;可以更新、修复或替换图层数据源&#xff0c;修改图层的符号系统&#xff0c;甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...