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.hqwc.cn/news/705346.html

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

相关文章

iOS plist文件增删改查

一. plist简介 plist文件&#xff0c;即属性列表文件&#xff0c;全名是Property List&#xff0c;这种文件的扩展名为.plist&#xff0c;因此&#xff0c;通常被叫做plist文件。它是一种用来存储串行化后的对象的文件&#xff0c;在iOS开发中通常用来存储用户设置&#xff0c…

记笔记从学Typora开始--------------------(1)下载、安装、购买、激活

一、登录Typora官网 官网地址&#xff1a;Typora 二、鼠标往下滑&#xff0c;点击下载按钮 三、下载得到安装包&#xff0c;双击 四、一直点击下一步&#xff0c;进行安装 五、安装完成 六、启动Typoera 七、针对欢迎界面点击下一页 八、一直点击直到弹出以下软件激活界面 九…

wefaf

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

NAT技术总结与双向NAT配置案例

NAT的转换方式&#xff1a; 1.静态转换&#xff1a;固定的一对一IP地址映射。 interface GigabitEthernet0/0/1 ip address 122.1.2.24 nat static global 122.1.2.1 inside 192.168.1.1 #在路由器出接口 公网地址 私网地址。 2.动态转换&#xff1a;Basic NAT nat address-gr…

Shell脚本 <<EOF ... EOF语法(Here Document)(特殊的输入重定向方式)(定界符)

文章目录 Here Document语法Here Document 的基本语法使用场景 关于定界符定界符不是变量定界符在 Here Document 中只是一个字符串&#xff0c;主要功能是标记输入文本的开始和结束&#xff0c;使用时应遵循最佳实践格式要求例子和说明如何使用定界符定界符可重复使用&#xf…

如何在云电脑实现虚拟应用—数据分层(应用分层)技术简介

数据分层&#xff08;应用分层&#xff09;技术简介 近几年虚拟化市场实现了非常大的发展&#xff0c;桌面虚拟化在企业中应用越来越广泛&#xff0c;其拥有的如下优点得到大量企业的青睐&#xff1a; 数据安全不落地。在虚拟化环境下面数据保存在中心服务器上面&#xff0c;…

想做好抖店?新手、老玩家切勿掉进这些坑,操作要慎重!

大家好&#xff0c;我是电商花花。 很多人都说做抖音小店不需要脑子&#xff0c;会抄就行&#xff0c;难道做店真的就是这样吗&#xff1f; 真的就是会抄&#xff0c;会简单选品&#xff0c;找一些达人就能出单&#xff0c;就能实现睡后收入了吗&#xff1f; 其实并不见得&a…

Day_5

1. Apache ECharts Apache ECharts 是一款基于 Javascript 的数据可视化图表库&#xff0c;提供直观&#xff0c;生动&#xff0c;可交互&#xff0c;可个性化定制的数据可视化图表 官网地址&#xff1a;https://echarts.apache.org/zh/index.html 入门案例 快速入门&#x…

K210开发板MicroPython开发环境搭建

一、安装CanMV IDE开发软件 1、进入如下连接 https://developer.canaan-creative.com/resource 2、点击下载 3、下一步 4、修改安装路径&#xff0c;下一步 5、接受许可下一步 6、下一步 7、安装 8、完成 9、区域①菜单栏&#xff1a;操作文件&#xff0c;使用工具等。…

短视频素材该去哪里找?分享8个短视频剪辑必备的素材大全

在短视频创作与运营中&#xff0c;素材的找寻常常成为创作者的最头疼的事&#xff0c;因为它既要求不侵犯版权&#xff0c;又追求内容的优质性。然而&#xff0c;今天我要为大家揭晓一些剪辑短视频素材库的宝藏网站&#xff0c;它们将成为你创作旅程中的得力帮手&#xff0c;让…

集群分发脚本xsync

1.环境准备 1.准备三台服务器&#xff08;我这里使用虚拟机,操作系统 CentOS7 &#xff09;它们的IP分别为 192.168.188.135、192.168.188.136、192.168.188.137 2.先将三台机器的主机名修改&#xff0c;为每台主机设置hostname&#xff08;具体名称由自己定义&#xff09;&am…

Canal解决select count(*)执行慢的问题

前言 count 的常用方式&#xff0c;使用 count(*)来统计数据条数&#xff0c;但是 innodb 没有存储数据总数&#xff0c;所以执行起来就会很慢。 可以使用 expalin sql 来返回预估行数&#xff0c;expalin select count(*)....., 通过预估的方式,统计数据条数。可以使用 redi…