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

ThreadLocal的使用

1. ThreadLocal介绍

ThreadLocal顾名思义,就是线程的本地变量,只有当前线程可见,对其他线程来说是封闭且隔离的。每一个线程为自己本身创建ThreadLocal变量,只有当前线程可以访问,其他的线程不可以,从根源上避免了多个线程对共享资源的竞争问题,提高程序的执行效率。

2. ThreadLocal的基本使用

这里以创建两个线程的ThreadLocal为例子,来说明ThreadLocal的基本使用,相关代码如下:

private static ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void print(String threadName) {System.out.println("线程名:" + threadName + " 线程变量:" + threadLocal.get());// 移除线程变量threadLocal.remove();}public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {threadLocal.set("local1");print("线程1");System.out.println("after remove:" + threadLocal.get());}}, "线程1").start();new Thread(new Runnable() {@Overridepublic void run() {threadLocal.set("local2");print("线程2");System.out.println("after remove:" + threadLocal.get());}}, "线程2").start();}

程序结果如下:
在这里插入图片描述
可以看到每一个线程都获取到了本身的线程变量,线程之间相互不影响。

3. ThreadLocal的实现原理

ThreadLocal如何实现线程变量之间互相不影响的呢?很简单,每一个线程都保存一份变量副本即可,下面将从设置值到取值的整个过程来说明。
ThreadLocal的设置值的方法是set, 源码如下:

public void set(T value) {// 获取当前线程对象Thread t = Thread.currentThread();// 获取ThreadLocalMap对象ThreadLocalMap map = getMap(t);if (map != null)// 不为空,直接将新值覆盖旧值map.set(this, value);else// 为空则进行初始化createMap(t, value);}ThreadLocalMap getMap(Thread t) {return t.threadLocals;}ThreadLocal.ThreadLocalMap threadLocals = null;

getMap方法返回的是每一个线程的threadLocals属性,threadLocals属性为ThreadLocal.ThreadLocalMap类型,保存每一个线程的ThreadLocal变量,其初始化在map=null的情况下进行,执行 createMap(t, value)方法进行初始化。

void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {table = new Entry[INITIAL_CAPACITY];// 通过hash运算计算出threadLocal的位置int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);table[i] = new Entry(firstKey, firstValue);size = 1;setThreshold(INITIAL_CAPACITY);}static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}

ThreadMap中的Entry的key为弱引用类型(这也就是为什么TheadLocal会存在内存泄漏的原因,后面解释)。当我们要进行取值时,则执行如下get方法,将ThreadLocal中的Entry取出,如果不存在,则会进行清理操作。

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();}
private Entry getEntry(ThreadLocal<?> key) {int i = key.threadLocalHashCode & (table.length - 1);Entry e = table[i];if (e != null && e.get() == key)return e;else// 执行清理操作return getEntryAfterMiss(key, i, e);}
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;}        private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);return value;}

4. ThreadLocal的内存泄漏问题的理解

ThreadLocal变量间的引用关系如下图所示:
在这里插入图片描述
当我们的ThreadLocal引用变成null时,由于ThreadMap的生命周期和当前线程一样,当前线程不结束,系统不会进行垃圾回收,这就造成一个现象:Entry中的key=null, 而value却存在,但我们无法在获取到value的值,这就造成内存泄漏。虽然在get方法中,当我们获取不到key的值时,会执行getEntryAfterMiss进行垃圾清理,但如果我们就一直访问key存在的Entry,getEntryAfterMiss方法就无法执行,内存泄漏还是存在,最稳妥的方法就是我们每次用完后都执行Remove操作,将变量手动清理。

相关文章:

ThreadLocal的使用

1. ThreadLocal介绍 ThreadLocal顾名思义&#xff0c;就是线程的本地变量&#xff0c;只有当前线程可见&#xff0c;对其他线程来说是封闭且隔离的。每一个线程为自己本身创建ThreadLocal变量&#xff0c;只有当前线程可以访问&#xff0c;其他的线程不可以&#xff0c;从根源…...

Java ~ Reference【总结】

一 概述 简介 在JDK1.2之前&#xff0c;Java中引用的定义是十分传统的&#xff1a;如果引用类型的变量中存储的数值代表的是另一块内存的起始地址&#xff0c;就称这块内存代表着一个引用。在这种定义之下&#xff0c;一个对象只有被引用和没有被引用两种状态。 实际上&#xf…...

最快方法求最长上升子序列(LIS)+最长公共子序列(LCS)模板(C/C++)

目录 1 LIS算法&#xff08;最长上升子序列&#xff09; 1.1 简介 1.2 代码 1.3 相关解释 2 LCS算法&#xff08;最长公共子序列&#xff09; 2.1 简介 2.2 代码&#xff08;动态规划&#xff0c;时间复杂度O&#xff08;nlogn&#xff09;&#xff09; 2.3 特殊…...

012+limou+C语言深入知识——(4)“结构体”与“枚举体”与“联合体”

一、结构体 1、结构体基础 &#xff08;1&#xff09;结构体完全声明 struct tag {member-list; }variable-list;//描述一个人 struct people {char name[10];//人名int age;//年龄int idnumber;//身份证 };&#xff08;2&#xff09;结构体不完全声明&#xff08;匿名结构体…...

Canvas百战成神-圆(1)

Canvas百战成神-圆 初始化容器 <canvas id"canvas"></canvas>canvas{border: 1px solid black; }让页面占满屏幕 *{margin: 0;padding: 0; } html,body{width: 100%;height: 100%;overflow: hidden; } ::-webkit-scrollbar{display: none; }初始化画笔…...

详解分库分表设计

详解分库分表设计 背景 ​ 在传统的单机数据库架构中&#xff0c;所有数据都存储在同一个数据库中&#xff0c;随着业务规模的不断扩大&#xff0c;数据量和并发量也会越来越大&#xff0c;这会给数据库的性能和可用性带来挑战。此外&#xff0c;当单机数据库的容量达到瓶颈时…...

动态规划-基础(斐波那契数、爬楼梯、使用最小花费爬楼梯、不同路径、不同路径II、整数拆分、不同的二叉搜索树)

动态规划&#xff0c;英文&#xff1a;Dynamic Programming&#xff0c;简称DP&#xff0c;如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。所以动态规划中每一个状态一定是由上一个状态推导出来的。动态规划问题&#xff0c;五步走&#xff1a;状态定义&am…...

深入理解WebSocket协议

“ 一直以来对WebSocket仅停留在使用阶段&#xff0c;也没有深入理解其背后的原理。当看到 x x x was not upgraded to websocket&#xff0c;我是彻底蒙了&#xff0c;等我镇定下来&#xff0c;打开百度输入这行报错信息&#xff0c;随即看到的就是大家说的跨域&#xff0c;或…...

Vector的扩容机制

到需要扩容的时候&#xff0c;Vector会根据需要的大小&#xff0c;创建一个新数组&#xff0c;然后把旧数组的元素复制进新数组。 我们可以看到&#xff0c;扩容后&#xff0c;其实是一个新数组&#xff0c;内部元素的地址已经改变了。所以扩容之后&#xff0c;原先的迭代器会…...

22讲MySQL有哪些“饮鸩止渴”提高性能的方法

短连接风暴 是指数据库有很多链接之后只执行了几个语句就断开的客户端&#xff0c;然后我们知道数据库客户端和数据库每次连接不仅需要tcp的三次握手&#xff0c;而且还有mysql的鉴权操作都要占用很多服务器的资源。话虽如此但是如果连接的不多的话其实这点资源无所谓的。 但是…...

10.0自定义SystemUI下拉状态栏和通知栏视图(六)之监听系统通知

1.前言 在进行rom产品定制化开发中,在10.0中针对systemui下拉状态栏和通知栏的定制UI的工作开发中,原生系统的下拉状态栏和通知栏的视图UI在产品开发中会不太满足功能, 所以根据产品需要来自定义SystemUI的下拉状态栏和通知栏功能,首选实现的就是下拉通知栏左滑删除通知的部…...

怎样在外网登录访问CRM管理系统?

一、什么是CRM管理系统&#xff1f; Customer Relationship Management&#xff0c;简称CRM&#xff0c;指客户关系管理&#xff0c;是企业利用信息互联网技术&#xff0c;协调企业、顾客和服务上的交互&#xff0c;提升管理服务。为了企业信息安全以及使用方便&#xff0c;企…...

Activity工作流(三):Service服务

3. Service服务 所有的Service都通过流程引擎获得。 3.1 RepositoryService 仓库服务是存储相关的服务&#xff0c;一般用来部署流程文件&#xff0c;获取流程文件&#xff08;bpmn和图片&#xff09;&#xff0c;查询流程定义信息等操作&#xff0c;是引擎中的一个重要的服务。…...

算法--最长回文子串--java--python

这个算法题里面总是有 暴力解法 把所有字串都拿出来判断一下 这里有小小的优化&#xff1a; 就是当判断的字串小于等于我们自己求得的最长回文子串的长度&#xff0c;那么我们就不需要在进行对这个的判断这里的begin&#xff0c;还可以用来取得最小回文子串是什么 java // 暴…...

ElasticSearch-第二天

目录 文档批量操作 批量获取文档数据 批量操作文档数据 DSL语言高级查询 DSL概述 无查询条件 叶子条件查询 模糊匹配 match的复杂用法 精确匹配 组合条件查询(多条件查询) 连接查询(多文档合并查询) 查询DSL和过滤DSL 区别 query DSL filter DSL Query方式查…...

【AI大比拼】文心一言 VS ChatGPT-4

摘要&#xff1a;本文将对比分析两款知名的 AI 对话引擎&#xff1a;文心一言和 OpenAI 的 ChatGPT&#xff0c;通过实际案例让大家对这两款对话引擎有更深入的了解&#xff0c;以便大家选择合适的 AI 对话引擎。 亲爱的 CSDN 朋友们&#xff0c;大家好&#xff01;近年来&…...

美团笔试-3.18

1、捕获敌人 小美在玩一项游戏。该游戏的目标是尽可能抓获敌人。 敌人的位置将被一个二维坐标 (x, y) 所描述。 小美有一个全屏技能&#xff0c;该技能能一次性将若干敌人一次性捕获。 捕获的敌人之间的横坐标的最大差值不能大于A&#xff0c;纵坐标的最大差值不能大于B。 现在…...

【12】SCI易中期刊推荐——计算机信息系统(中科院4区)

🚀🚀🚀NEW!!!SCI易中期刊推荐栏目来啦 ~ 📚🍀 SCI即《科学引文索引》(Science Citation Index, SCI),是1961年由美国科学信息研究所(Institute for Scientific Information, ISI)创办的文献检索工具,创始人是美国著名情报专家尤金加菲尔德(Eugene Garfield…...

好不容易约来了一位程序员来面试,结果人家不做笔试题

感觉以后还是不要出面试题这环节好了。好不容易约来了一位程序员来面试。刚递给他一份笔试题&#xff0c;他一看到要做笔试题&#xff0c;说不做笔试题&#xff0c;有问题面谈就好了&#xff0c;搞得我有点尴尬&#xff0c;这位应聘者有3年多工作经验。关于程序员岗位&#xff…...

这几个过时Java技术不要再学了

Java 已经发展了近20年&#xff0c;极其丰富的周边框架打造了一个繁荣稳固的生态圈。 Java现在不仅仅是一门语言&#xff0c;而且还是一整个生态体系&#xff0c;实在是太庞大了&#xff0c;从诞生到现在&#xff0c;有无数的技术在不断的推出&#xff0c;也有很多技术在不断的…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

mongodb源码分析session执行handleRequest命令find过程

mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程&#xff0c;并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令&#xff0c;把数据流转换成Message&#xff0c;状态转变流程是&#xff1a;State::Created 》 St…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)

Aspose.PDF 限制绕过方案&#xff1a;Java 字节码技术实战分享&#xff08;仅供学习&#xff09; 一、Aspose.PDF 简介二、说明&#xff08;⚠️仅供学习与研究使用&#xff09;三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...