ThreadLocal,一次到位

一、定义

ThreadLocal是线程私有变量,用于保存每个线程的私有数据。

那么什么情况下需要进行线程隔离

二、源码分析

public class ThreadLocalTest01 {ThreadLocal<Integer> t = new ThreadLocal<>();public  void test() {t.set(1);Integer integer = t.get();}
}

set

在调用set方法时,会先获取当前线程,然后获取当前线程的ThreadLocalMap,判断map是否存在,若不存在,则把创建一个ThreadLocalMap,若存在,则以当前的ThreadLocal作为key,传入的参数作为value存入map中。

    public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}}

其中ThreadLocalMap是ThreadLocal的内部类,它有一个静态内部类Entry,继承自WeakReference

static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}

get()

获取当前线程,获取当前线程的ThreadLocalMap,然后用当前的ThreadLocal作为key 去map中查找,如果存在对应的Entry,那么就返回Entry中的value,否则就会执行初始化并返回默认值,其实就是null

    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 T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}if (this instanceof TerminatingThreadLocal) {TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);}return value;}
    protected T initialValue() {return null;}

三、注意的问题

3.1 为什么使用弱引用

主要两个原因
1 . 没有手动删除这个 Entry
2 . CurrentThread 当前线程依然运行
原因是使用ThreadLocal有可能会导致内存泄漏,使用弱引用能解决一部分内存泄漏
第一点很好理解,只要在使用完下 ThreadLocal ,调用其 remove 方法删除对应的 Entry ,就能避免内存泄漏。

第二点稍微复杂一点,由于ThreadLocalMap 是 Thread 的一个属性,被当前线程所引用,所以ThreadLocalMap的生命周期跟 Thread 一样长。如果threadlocal变量被回收,那么当前线程的threadlocal 变量副本指向的就是key=null, 也即entry(null,value),那这个entry对应的value永远无法访问到。实际私用ThreadLocal场景都是采用线程池,而线程池中的线程都是复用的,这样就可能导致非常多的entry(null,value)出现,从而导致内存泄露。

综上, ThreadLocal 内存泄漏的根源是:
由于ThreadLocalMap 的生命周期跟 Thread 一样长,对于重复利用的线程来说,如果没有手动删除(remove()方法)对应 key 就会导致entry(null,value)的对象越来越多,从而导致内存泄漏.

3.1.1 、key 如果是强引用

 那么为什么ThreadLocalMap的key要设计成弱引用呢?其实很简单,如果key设计成强引用且没有手动remove(),那么key会和value一样伴随线程的整个生命周期。

1、假设在业务代码中使用完ThreadLocal, ThreadLocal ref被回收了,但是因为threadLocalMap的Entry强引用了threadLocal(key就是threadLocal), 造成ThreadLocal无法被回收。在没有手动删除Entry以及CurrentThread(当前线程)依然运行的前提下, 始终有强引用链CurrentThread Ref → CurrentThread →Map(ThreadLocalMap)-> entry, Entry就不会被回收( Entry中包括了ThreadLocal实例和value), 导致Entry内存泄漏也就是说: ThreadLocalMap中的key使用了强引用, 是无法完全避免内存泄漏的。请结合图1看。

3.1.2 那么为什么 key 要用弱引用

 事实上,在 ThreadLocalMap 中的set/getEntry 方法中,会对 key 为 null(也即是 ThreadLocal 为 null )进行判断,如果为 null 的话,那么会把 value 置为 null 的.这就意味着使用threadLocal , CurrentThread 依然运行的前提下.就算忘记调用 remove 方法,弱引用比强引用可以多一层保障:弱引用的 ThreadLocal 会被回收.对应value在下一次 ThreadLocaI 调用 get()/set()/remove() 中的任一方法的时候会被清除,从而避免内存泄漏.

3.2 发生Hash冲突

ThreadLocalMap的结构非常简单只用一个数组存储,并没有链表结构,当出现Hash冲突时采用线性查找的方式,所谓线性查找,就是根据初始key的hashcode值确定元素在table数组中的位置,如果发现这个位置上已经有其他key值的元素被占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。如果产生多次hash冲突,处理起来就没有HashMap的效率高,为了避免哈希冲突,使用尽量少的threadlocal变量。

四、实际项目中的使用

每个用户调用我们的项目时,都会创建一个新的HTTP请求线程,这一次请求中会调用类A的方法testA()和B.class中的testB()方法,且都需要用到当前用户的某些信息,如用户名、Cookie、账号密码等信息。
在这里插入图片描述
在这里插入图片描述
调用两次接口传入不同的值,可以看到这一次请求中获得的值是一致的,而不同的调用请求获得的值是不同的。
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

传输层协议——TCP协议

TCP协议又叫传输控制协议&#xff0c;TCP/IP协议是计算机通信网络中目前使用最多的协议&#xff0c;同时也融入了生活的方方面面&#xff0c;不管是浏览网页使用的http/https协议、物联网设备使用的MQTT/MQTTS协议与下载文件使用的ftp协议、工业以太网中使用的Modbus TCP协议等…

JVM学习-虚拟机栈

虚拟机栈 每个线程创建时都会创建一个虚拟机栈&#xff0c;其内部保存一个个栈帧&#xff0c;对应一次次Java方法调用&#xff0c;栈是线程私有的。 生命周期: 与线程相同 作用 主管Java程序的运行&#xff0c;它保存方法的局部变量、部分结果、并参与方法的调用和返回。 …

PyTorch之list、ndarray、tensor数据类型相互转换

温故而知新&#xff0c;可以为师矣&#xff01; 一、参考资料 python中list、numpy、torch.tensor之间的相互转换 二、常用操作 list 转 numpy ndarray np.array(list) import numpy as npa_list [[j for j in range(5)] for i in range(3)] a_ndarray np.array(a_lis…

云服务器配置mysql允许被远程连接从而使用图形化界面

介绍 在云服务器上搭建和配置数据库是进行网站和应用开发的关键步骤之一。本文将介绍如何在云服务器上设置 MySQL 8 和 MySQL 5&#xff0c;以允许远程连接&#xff0c;从而让你的数据库能够被远程用户访问。这样你的本机就可以访问linux服务器上的mysql能&#xff0c;就可以使…

254 基于matlab的钢筋混凝土非线性分析

基于matlab的钢筋混凝土非线性分析&#xff0c;根据梁本构关系&#xff0c;然后进行非线性分析&#xff0c;绘制弯矩-曲率曲线。可设置梁的截面尺寸、混凝土本构&#xff0c;钢筋截面面积等相关参数&#xff0c;程序已调通&#xff0c;可直接运行。 254 钢筋混凝土非线性分析 弯…

利用管道通信(pipe)测量进程间的上下文切换(context switch)开销

利用管道通信(pipe)测量进程间的上下文切换(context switch)开销 《https://pages.cs.wisc.edu/~remzi/OSTEP/cpu-mechanisms.pdf》 Measuring the cost of a context switch is a little trickier. The lmbench benchmark does so by running two processes on a single CPU…

qmake、CMake、make和Makefile

为了跟踪C工程的全部部分&#xff0c;要求有一种机制来精确地指定&#xff1a; 涉及的输入文件&#xff0c;如源代码文件&#xff1a;.cpp&#xff0c;头文件&#xff1a;.h建立程序时所需的工具&#xff0c;如编译器&#xff1a; g.exe&#xff0c;链接器&#xff1a;ld.exe&a…

哈夫曼编码的应用

数据结构与算法课的一个简单实验&#xff0c;记录一下&#xff0c;以供参考。 文章目录 要求测试样例统计字母出现次数建立哈夫曼树对字符编码对原文进行编码译码 要求 输入一段100—200字的英文短文&#xff0c;存入一文件a中。统计短文出现的字母个数n及每个字母的出现次数…

终于搞懂Linux 设备树中的#address-cells,#size-cells 和reg 属性

目录 一、前置知识 1. 处理器平台2. reg 属性的基本格式3. reg 属性的作用 reg 用法 二、#address-cells 和 #size-cells 属性 1. 示例1 2. 示例23. 示例3 一、前置知识 要理解#address-cells和#size-cell 这两个属性&#xff0c;就要先了解 reg属性。 1. 处理器平台 下…

VS2022如何添加现有项

以 想在队列里&#xff0c;使用堆栈的.c&#xff0c;.h文件 为例 目录 1.复制堆栈的.c&#xff0c;.h文件 ​编辑 2.打开队列所在项目的文件夹 3.粘贴堆栈的.c&#xff0c;.h文件 4.在头文件和源文件添加相应的堆栈的.c&#xff0c;.h文件 1.复制堆栈的.c&#xff0c;.h文件…

QML进阶(十七) ECMAScript 语法介绍

文章目录 基本语法变量基本类型类型转换对象函数和循环打印输出内置对象来自QML的基本类型ECMAScript语言的标准是由Netscape、Sun、微软、Borland等公司基于JavaScript和JScript定义出来的脚本语言标准。可以为不同种类的浏览器环境提供核心的脚本编程能力。在QML中我们通过EC…

HCIP【VLAN综合实验】

目录 一、实验拓扑图&#xff1a; 二、实验要求&#xff1a; 三、实验思路&#xff1a; 四、实验步骤&#xff1a; 1、在交换机SW1,SW2,SW3配置VLAN和各个接口对应类型的配置 2、在路由器上面配置DHCP服务 一、实验拓扑图&#xff1a; 二、实验要求&#xff1a; 1、PC1 …

STK12 RPO模块学习(3)

一、Maintain NMC RPO Sequence Maintain Natural Motion Circumnavigation RPO序列在目标星和追踪星经历不同的力的情况下保持NMC。通常这种差异是由于阻力和太阳光压造成的。这些是主要不同力当执行接近任务的时候&#xff0c;因为重力和相对三体摄动力非常小当相对距离在10…

【Docker使用技巧】

Docker使用 命令 //docker 进入容器内部 退出 exit docker exec -it 容器ID /bin/bash //docker 查看容器日志 退出 Ctrlc docker logs [OPTIONS] 容器ID或名称 [OPTIONS] -f : 跟踪日志输出 -t : 显示时间戳 --tail :仅列出最新N条容器日志 --since&#xff1a;显示某个日…

从零开始精通RTSP之传输AAC音频流

概述 AAC&#xff0c;英文全称为Advanced Audio Coding&#xff0c;是一种高效的有损音频压缩格式&#xff0c;由MPEG-4标准定义。相比传统的MP3&#xff0c;AAC在相同比特率下能提供更好的音质&#xff0c;尤其在低比特率场景下优势明显。AAC支持多种采样率、声道数和编码工具…

MySQL远程连接错误解决:“Host is not allowed to connect to this MySQL server”详解

目录 一、异常错误二、原因三、解决方法 一、异常错误 通过远程客户端访问MySQL服务器时会遇到“Host is not allowed to connect to this MySQL server”的错误提示。 二、原因 MySQL服务器当前配置不允许来自特定主机的连接尝试。 三、解决方法 允许远程主机访问MySQL服…

link.click()时浏览器报错The file at ‘data:image/png;base64,iVBORw

代码如下&#xff1a; const dataURL canvas.toDataURL({format: "png",width: 400,height: 400, });const link document.createElement("a"); link.download new Date().getTime();link.href dataURL; document.body.appendChild(link); link.click…

IT行业的现状与未来发展趋势

方向一&#xff1a;技术革新与行业应用 随着科技的日新月异&#xff0c;IT行业已成为全球经济增长和社会进步的重要引擎。从云计算、大数据、人工智能到物联网、5G通信和区块链&#xff0c;这些前沿技术正以前所未有的速度重塑我们的生活和工作方式。以下是我对IT行业现状及未来…

高压无源探头能测整流桥电压吗?

高压无源探头是用于测量高电压电路中信号的一种工具&#xff0c;它不需要外部电源供电。然而&#xff0c;对于测量整流桥电压&#xff0c;需要考虑几个因素以确定是否可以使用高压无源探头。 首先&#xff0c;让我们了解一下整流桥的基本原理。整流桥是一种电路&#xff0c;用…

MyBatis的一二级缓存区别

MyBatis的一级缓存和二级缓存之间存在以下主要区别&#xff1a; 缓存级别与作用域&#xff1a; 一级缓存&#xff1a;也称为SqlSession级别的缓存&#xff0c;与数据库会话&#xff08;SqlSession对象&#xff09;绑定&#xff0c;并且默认开启。一级缓存的作用域仅限于同一个…