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

ThreadLocal 释放的方式有哪些

ThreadLocal基础概念:IT-BLOG-CN

ThreadLocalJava中用于在同一个线程中存储和隔离变量的一种机制。通常情况下,我们使用ThreadLocal来存储线程独有的变量,并在任务完成后通过remove方法清理这些变量,以防止内存泄漏。然而,在使用线程池时,线程会被重用,这可能导致ThreadLocal变量未被及时清理,从而引发内存泄漏问题。

除了直接调用ThreadLocalremove方法外,还有一些其他方式可以帮助释放ThreadLocal变量:

一、在线程池中使用自定义的ThreadFactory

创建一个自定义的ThreadFactory,在创建线程时添加钩子,以便在任务完成后清理ThreadLocal变量。扩展:搭建统一线程池平台,对该部分进行了改造。提供多个工厂,就包含自动清理工厂。

import java.util.concurrent.ThreadFactory;public class CleaningThreadFactory implements ThreadFactory {private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();@Overridepublic Thread newThread(Runnable r) {return defaultFactory.newThread(() -> {try {r.run();} finally {// 清理ThreadLocal变量ThreadLocalHolder.clear();}});}
}

这里的ThreadLocalHolder就是所有ThreadLocal的一个管理类,这里举个例子:

public class ThreadLocalHolder {
private static final ThreadLocal<AggAlibabaRerQueryResponse> TL_AGG_REF_RER = new ThreadLocal<>();
private static final ThreadLocal<OpenAlibabaSearchResponse> TL_ORDER_DETAIL = new ThreadLocal<>();// get/set 只流一个参考
public static void setAggRefRer(AggAlibabaRerQueryResponse aggRefRer) {
TL_AGG_REF_RER.set(aggRefRer);
}public static FlightRefRerQueryResponse getFlightRefRer() {return TL_FLIGHT_REF_RER.get();
}/*** 用于清空threadlocal,否则会有内存泄漏*/public static void clear() {TL_AGG_REF_RER.remove();TL_ORDER_DETAIL.remove();
}

二、使用ThreadPoolExecutor的钩子方法

可以扩展ThreadPoolExecutor并覆盖其beforeExecuteafterExecute方法,以便在任务执行前后进行清理操作。

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;public class CleaningThreadPoolExecutor extends ThreadPoolExecutor {public CleaningThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, LinkedBlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);}@Overrideprotected void beforeExecute(Thread t, Runnable r) {super.beforeExecute(t, r);// 清理之前的ThreadLocal变量ThreadLocalHolder.clear();}@Overrideprotected void afterExecute(Runnable r, Throwable t) {super.afterExecute(r, t);// 清理当前的ThreadLocal变量ThreadLocalHolder.clear();}
}

三、使用装饰器模式包装RunnableCallable

可以创建一个装饰器,包装RunnableCallable任务,在任务执行前后进行清理操作。

import java.util.concurrent.Callable;public class CleaningRunnable implements Runnable {private final Runnable task;public CleaningRunnable(Runnable task) {this.task = task;}@Overridepublic void run() {try {task.run();} finally {// 清理ThreadLocal变量ThreadLocalHolder.clear();}}
}public class CleaningCallable<V> implements Callable<V> {private final Callable<V> task;public CleaningCallable(Callable<V> task) {this.task = task;}@Overridepublic V call() throws Exception {try {return task.call();} finally {// 清理ThreadLocal变量ThreadLocalHolder.clear();}}
}

四、使用ThreadLocal的子类

可以创建一个ThreadLocal的子类,并在任务完成后自动清理变量。可以通过覆盖initialValue方法来实现:finalize 出发的时机是在gc的时候,但是finalize方法在现代Java开发中并不推荐使用,因为它的执行时间和执行顺序是不确定的。

public class AutoCleanupThreadLocal<T> extends ThreadLocal<T> {@Overrideprotected void finalize() throws Throwable {this.remove();super.finalize();}
}

五、TheadLocal 实际使用案例

将整个流程中需要用到的接口数据都存储起来,这个流程中调用链路比较深,同时也存在并发的操作,可以使用ThreadLocal

public class ThreadLocalHolder {
private static final ThreadLocal<AggAlibabaRerQueryResponse> TL_AGG_REF_RER = new ThreadLocal<>();
private static final ThreadLocal<OpenAlibabaSearchResponse> TL_ORDER_DETAIL = new ThreadLocal<>();
private static final ThreadLocal<FlightAlibabaResponse> TL_FLIGHT_REF_RER = new ThreadLocal<>();
private static final ThreadLocal<XOrderAlibabaInfo> TL_X_ORDER_DETAIL = new ThreadLocal<>();
private static final ThreadLocal<FlightAlibabaResponseBodyType> TL_DOM_FLIGHT_SEARCH_RESULT = new ThreadLocal<>();
private static final ThreadLocal<ResponseAlibabaType> TL_RESCHEDULE_FLIGHT_SEARCH_RESULT = new ThreadLocal<>();// get/set 只流一个参考
public static void setAggRefRer(AggAlibabaRerQueryResponse aggRefRer) {
TL_AGG_REF_RER.set(aggRefRer);
}public static FlightRefRerQueryResponse getFlightRefRer() {return TL_FLIGHT_REF_RER.get();
}/*** 用于清空threadlocal,否则会有内存泄漏*/public static void clear() {TL_AGG_REF_RER.remove();TL_ORDER_DETAIL.remove();TL_FLIGHT_REF_RER.remove();TL_X_ORDER_DETAIL.remove();TL_DOM_FLIGHT_SEARCH_RESULT.remove();TL_RESCHEDULE_FLIGHT_SEARCH_RESULT.remove();
}

六、基础支持补充 ----- ThreadLocal 的实现原理

下面是ThreadLocal的类图结构,从图中可知:Thread类中有两个变量threadLocalsinheritableThreadLocals,二者都是ThreadLocal内部类ThreadLocalMap类型的变量,我们通过查看内部类ThreadLocalMap可以发现实际上它类似于一个HashMap。在默认情况下,每个线程中的这两个变量都为null

ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

只有当线程第一次调用ThreadLocalset或者get方法的时候才会创建他们(后面我们会查看这两个方法的源码)。除此之外,每个线程的本地变量不是存放在ThreadLocal实例中,而是放在调用线程的ThreadLocals变量里面(前面也说过,该变量是Thread类的变量)。也就是说,ThreadLocal类型的本地变量是存放在具体的线程空间上,相当于一个装载本地变量的工具壳,通过set方法将value添加到调用线程的threadLocals中,当调用线程调用get方法时候能够从它的threadLocals中取出变量。如果调用线程一直不终止,那么这个本地变量将会一直存放在他的threadLocals中,所以不使用本地变量的时候需要调用remove方法将threadLocals中删除不用的本地变量。下面我们通过查看ThreadLocalsetget以及remove方法来查看ThreadLocal具体实怎样工作的。

【1】set方法源码

public void set(T value) {//(1)获取当前线程(调用者线程)Thread t = Thread.currentThread();//(2)以当前线程作为key值,去查找对应的线程变量,找到对应的mapThreadLocalMap map = getMap(t);//(3)如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值if (map != null)map.set(this, value);//(4)如果map为null,说明首次添加,需要首先创建出对应的mapelsecreateMap(t, value);
}

在上面的代码中,(2)处调用getMap方法获得当前线程对应的threadLocals(参照上面的图示和文字说明),该方法代码如下:

ThreadLocalMap getMap(Thread t) {return t.threadLocals; //获取线程自己的变量threadLocals,并绑定到当前调用线程的成员变量threadLocals上
}

如果调用getMap方法返回值不为null,就直接将value值设置到threadLocals中(key为当前线程引用,值为本地变量);如果getMap方法返回null说明是第一次调用set方法(前面说到过,threadLocals默认值为null,只有调用set方法的时候才会创建map),这个时候就需要调用createMap方法创建 threadLocals,该方法如下所示:createMap方法不仅创建了threadLocals,同时也将要添加的本地变量值添加到了threadLocals中。

void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}

【2】get方法源码:get方法的实现中,首先获取当前调用者线程,如果当前线程的threadLocals不为null,就直接返回当前线程绑定的本地变量值,否则执行setInitialValue方法初始化threadLocals变量。在setInitialValue方法中,类似于set方法的实现,都是判断当前线程的threadLocals变量是否为null,是则添加本地变量(这个时候由于是初始化,所以添加的值为null),否则创建threadLocals变量,同样添加的值为null

public T get() {//(1)获取当前线程Thread t = Thread.currentThread();//(2)获取当前线程的threadLocals变量ThreadLocalMap map = getMap(t);//(3)如果threadLocals变量不为null,就可以在map中查找到本地变量的值if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//(4)执行到此处,threadLocals为null,调用该更改初始化当前线程的threadLocals变量return setInitialValue();
}private T setInitialValue() {//protected T initialValue() {return null;}T value = initialValue();//获取当前线程Thread t = Thread.currentThread();//以当前线程作为key值,去查找对应的线程变量,找到对应的mapThreadLocalMap map = getMap(t);//如果map不为null,就直接添加本地变量,key为当前线程,值为添加的本地变量值if (map != null)map.set(this, value);//如果map为null,说明首次添加,需要首先创建出对应的mapelsecreateMap(t, value);return value;
}

【3】remove方法的实现: remove方法判断当前线程对应的threadLocals变量是否为null,不为null就直接删除当前线程中指定的threadLocals变量。

public void remove() {//获取当前线程绑定的threadLocalsThreadLocalMap m = getMap(Thread.currentThread());//如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量if (m != null)m.remove(this);
}private void remove(ThreadLocal<?> key) {Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {if (e.refersTo(key)) {e.clear();expungeStaleEntry(i);return;}}
}

【4】如下图所示: 每个线程内部有一个名为threadLocals的成员变量,该变量的类型为ThreadLocal.ThreadLocalMap类型(类似于一个HashMap),其中的key为当前定义的ThreadLocal变量的this引用,value为我们使用set方法设置的值。每个线程的本地变量存放在自己的本地内存变量threadLocals中,如果当前线程一直不消亡,那么这些本地变量就会一直存在(可能会导致内存溢出),因此使用完毕需要将其remove掉。

相关文章:

ThreadLocal 释放的方式有哪些

ThreadLocal基础概念&#xff1a;IT-BLOG-CN ThreadLocal是Java中用于在同一个线程中存储和隔离变量的一种机制。通常情况下&#xff0c;我们使用ThreadLocal来存储线程独有的变量&#xff0c;并在任务完成后通过remove方法清理这些变量&#xff0c;以防止内存泄漏。然而&…...

监控-zabbix

1运维监控 是指对计算机系统、网络、服务器等关键IT基础设施进行实时监控&#xff0c;以确保系统的稳定运行和及时发现潜在问题 2老监控框架&#xff08;不会用但需要知道&#xff09; Cacti&#xff1a; Cacti是一款基于PHP、MySQL开发的网络流量监测图形分析工具。主要监…...

设计模式 解释器模式(Interpreter Pattern)

文章目录 解释器模式简绍解释器模式的结构优缺点UML图具体代码实现Context 数据实体类&#xff0c;可以包含一些方法Abstract Expression 创建接口方法Terminal Expression 对数据简单处理Non-Terminal Expression 同样实现抽象接口方法Client&#xff08;客户端&#xff09; 调…...

Linux echo命令讲解及与重定向符搭配使用方法,tail命令及日志监听方式详解

echo echo具有回声&#xff0c;回响的意思&#xff0c;在linux系统中echo一般可以输出指定字符或用于命令执行 echo命令的用法为 echo 输出字符串 或 echo 命令 若参数为字符串则进行字符串输出&#xff0c;注意若字符串中含空格最好将其用引号括起&#xff0c;防止echo命…...

Linux网络:总结协议拓展

1. TCP/IP四层模型总结 2. 网络协议拓展 DNS协议&#xff08;地址解析协议&#xff09; TCP/IP使用IP地址和端口号来确定网络中一台主机的一个程序。 但是这样标定不方便记忆&#xff0c;于是开始引出主机名&#xff08;字符串&#xff09;&#xff0c;使用hosts文件来描述…...

去除恢复出厂设置中UI文字显示

文章目录 需求场景 一、代码跟踪与分析在线文字搜索RK平台本地源码搜索实际测试验证代码推理 二、实现方案三、延伸知识四、知识总结 需求 需求&#xff1a;去除恢复出厂设置中UI文字显示 场景 Android 相关产品各种方向旋转、强制横竖屏等需求&#xff0c;导致在恢复出厂设…...

《高校教育管理》

《高校教育管理》为中文社会科学引文索引&#xff08;CSSCI&#xff09;来源期刊、北大中文核心期刊、RCCSE中国核心学术期刊、人大“复印报刊资料”重要转载来源期刊&#xff0c;是江苏大学主办&#xff0c;中国高等教育管理研究会协办的全国性高等教育管理专业期刊。 ISSN 1…...

全国计算机二级考试C语言篇4——选择题

运算符与表达式 1.赋值的正确写法 赋值操作是一个很常见的操作&#xff0c;但是赋值操作也有一些需要注意的地方。赋值操作是将一个表达式的值赋给一个变量的过程。在C语言中&#xff0c;赋值操作符是""。结合性从右到左&#xff0c;不控制求值顺序。 下面是几种C语言…...

数据结构————哈希表

哈希表&#xff08;Hash table&#xff09;&#xff0c;也被称为散列表&#xff0c;是一种根据关键值&#xff08;Key value&#xff09;而直接进行访问的数据结构。它通过把关键值映射到表中的一个位置来访问记录&#xff0c;从而加快查找的速度。这个映射函数被称为散列函数或…...

element select + tree

element select tree的使用 <template slot"action1" slot-scope"text, record, index"><el-select v-model"record.tagValue" multiple placeholder"请选择":filter-method"(e) > filterTree(e, index)" filt…...

LeetCode之矩阵

36. 有效的数独 class Solution {// 方法 isValidSudoku 接收一个字符二维数组 board&#xff0c;表示数独棋盘&#xff0c;返回是否有效public boolean isValidSudoku(char[][] board) {// 创建三个二维数组来记录每一行、列和子框中数字的出现次数int[][] rows new int[9][…...

Windows文件系统介绍与基本概念解析

1. 引言 1.1 什么是文件系统 文件系统是一种管理和组织计算机存储设备上文件和文件夹的方法。它提供了文件的创建、读取、写入、删除等操作,并负责文件在存储设备上的存储和访问。 在操作系统中,文件系统是一个重要的组成部分,它使得用户可以方便地使用计算机存储设备来存…...

使用 Apache POI 实现 Java Word 模板占位符替换功能

使用 Apache POI 实现 Java Word 模板占位符替换功能 在日常开发中&#xff0c;我们经常会遇到生成 Word 文档的需求&#xff0c;特别是在需要从模板导出 Word 文件时&#xff0c;比如生成合同、报告等。通过使用模板&#xff0c;开发者可以减少重复的工作&#xff0c;将预定义…...

第三届人工智能与智能信息处理国际学术会议(AIIIP 2024)

目录 大会介绍 基本信息 合作单位 主讲嘉宾 会议组委 征文主题 ​ 参会方式 会议日程 中国-天津 | 2024年10月25-27日 | 会议官网&#xff1a;www.iiip.net 大会介绍 第三届人工智能与智能信息处理国际学术会议&#xff08;AIIIP 2024&#xff09;将于202…...

【动手学深度学习】04 数据操作 + 数据预处理(个人向笔记)

数据操作 N维数组是机器学习和神经网络的主要数据结构其中 2-d 矩阵中每一行表示每一行表示一个样本 当维度来到三维的时候则可以表示成一张图片&#xff0c;再加一维就可以变成多张图片&#xff0c;再加一维则可以变成一个视频 访问元素 冒号表示从冒号左边的元素到冒号右…...

本地搭建 Whisper 语音识别模型

Whisper 是由 OpenAI 开发的一款强大的语音识别模型&#xff0c;具有出色的多语言处理能力。搭建和使用 Whisper 模型可以帮助您将音频内容转换为文本&#xff0c;这在语音转写、语音助手、字幕生成等应用中都具有广泛的用途。本指南将对如何在本地环境中搭建 Whisper 语音识别…...

数据集成-缝合一套数据仓库Infra的臆想

一、数据集成当前困境 目前数据集成基础设施建设仅一个单一数据库&#xff0c;无法很好支持上层应用的建设步骤&#xff0c;继续采用当前设施跟随产品的策略&#xff0c;数据产品开发受限巨大&#xff0c;从目前实施的几个产品看&#xff0c;存在以下主要问题&#xff1a; 功能…...

运营有哪几种?

运营又有很多类&#xff0c;分为&#xff1a;内容运营、用户运营、活动运营、产品运营、新媒体运营、社群运营、电商运营、短视频运营 1.内容运营&#xff1a; 做内容提升各类数据&#xff0c;比如内容的数量/浏览数量/互动数传播数等。 适合人群&#xff1a;适合喜欢看文章热…...

Android视频编辑:利用FFmpeg实现高级功能

在移动设备上进行视频编辑的需求日益增长&#xff0c;用户期望能够在智能手机或平板电脑上轻松地编辑视频&#xff0c;以满足社交媒体分享或个人存档的需求。Android平台因其广泛的用户基础和开放的生态系统&#xff0c;成为视频编辑应用的理想选择。FFmpeg&#xff0c;作为一个…...

图片无损缩放PhotoZoom Pro 9.0.2绿色版 +免费赠送PhotoZoom激活优惠代码

PhotoZoom Pro 9.0.2 是一款专业的图片无损缩放软件&#xff0c;该软件采用了 benvista s-spline 独特技术&#xff0c;增强了对图像格式的支持&#xff0c;多处理器支持&#xff0c;GPU 加速&#xff0c;win10和 Photoshop CC 支持。带来一流的数字图形扩展与缩减技术。该软件…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

k8s从入门到放弃之Ingress七层负载

k8s从入门到放弃之Ingress七层负载 在Kubernetes&#xff08;简称K8s&#xff09;中&#xff0c;Ingress是一个API对象&#xff0c;它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress&#xff0c;你可…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

Webpack性能优化:构建速度与体积优化策略

一、构建速度优化 1、​​升级Webpack和Node.js​​ ​​优化效果​​&#xff1a;Webpack 4比Webpack 3构建时间降低60%-98%。​​原因​​&#xff1a; V8引擎优化&#xff08;for of替代forEach、Map/Set替代Object&#xff09;。默认使用更快的md4哈希算法。AST直接从Loa…...