ThreadLocal解析

news2024/9/25 9:33:46

ThreadLocal是一个存储线程本地变量的对象,在ThreadLocal中存储的对象在其他线程中是不可见的,本文介绍ThreadLocal的原理。

1、threadLocal使用

有如下代码:

@Slf4j
public class TestThreadLocal {
    public static void main(String[] args) {
        ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        threadLocal.set(999);
        log.info(threadLocal.get().toString());
		//使用线程池创建一个线程
        ExecutorService service = Executors.newSingleThreadExecutor();
        service.execute(()->{
            log.info(threadLocal.get().toString());//threadLocal.get()将为null
            threadLocal.set(888);
            log.info(threadLocal.get().toString());
        });

        log.info(threadLocal.get().toString());
    }
}
输出:
2023-03-09 09:03:40.572 [main] INFO -- 999
2023-03-09 09:03:40.598 [main] INFO -- 999
Exception in thread "pool-1-thread-1" java.lang.NullPointerException
	at com.iwat.arithmetic.thread.TestThreadLocal.lambda$main$0(TestThreadLocal.java:24)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

从结果中可以看到,在新线程中无法获取到999,祝线程中set的数,只有祝线程能get到,这就保证了变量的线程唯一。

2、原理

原理就要看源码
首先看threadLocal.set(999);的源码

	//ThreadLocal类中
    public void set(T value) {
    	//1.获取当前线程
        Thread t = Thread.currentThread();
        //2. 获取线程中的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null)
        	//线程中的ThreadLocalMap对象不为空,直接set值,this就是当前ThreadLocal对象没,值就是值
            map.set(this, value);
        else
        	//线程中的ThreadLocalMap对象为空,创建并set值
            createMap(t, value);
    }

很简单,获取当前线程,然后获取当前线程的ThreadLocalMap对象,这里的ThreadLocalMap可以就当做一个Map处理,但是这个Map不一样的地方在于key只能是ThreadLocal对象。现在我们就可以分析一下了。

在这里插入图片描述

每一线程都有一个ThreadLocalMap对象,对于每一个ThreadLocal对象来说它可以在每一个线程中ThreadLocalMap中存一个以它为key的值。例如:

  • 在线程1中,ThreadLocal对象1执行set方法,实际上就是在线程1中的ThreadLocalMap中添加一个k-v(1号红色箭头),k就是ThreadLocal对象1。
  • 在线程1中,ThreadLocal对象2执行get方法,就是在线程1的ThreadLocalMap中获取以ThreadLocal对象2为key的值(2号红色箭头)

每个ThreadLocal在某个线程中只能有一个对应value,因为Map中key不能重复,这就实现了线程唯一变量的功能。

下面看一下ThreadLocal中的get方法验证一下分析:

	//ThreadLocal类中
    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();
    }

同时这也解释了,为什么上面代码中出现Exception in thread "pool-1-thread-1" java.lang.NullPointerException,原因就是在新创建的线程中的ThreadLocalMap中没有以当前ThreadLocal对象为key的值。

3、java引用类型

3.1 GC回收对象

java垃圾回收器多采用可达性分析算法来确定一个java对象需不需要回收,过程是这样的:

  • 首先确定一些一定不能回收的对象,叫做GCRoot对象
  • 查找GCRoot对象引用的对象
  • 然后沿着引用链一直找(注意:引用有多种类型)

最终根据对象被引用的类型、有没有被引用,来决定是不是回收这个对象。

3.2 引用类型

那么引用类型有哪些呢?总体而言分为四种:

  1. 强引用
    沿着GC Root引用链可以找到的对象就是强引用对象
  2. 软引用 (SoftReference)
    垃圾回收后仍内存不足,就会回收掉
  3. 弱号1用 (WeakReference)
    垃圾回收就将其回收掉
  4. 虚引用 (PhantomReference)
    必须配合引用队列使用,主要配合 ByteButfer 使用,被引用对象回收时,会将虛引用入队,由
    Reference Handler 线程调用虚引用相关方法释放直接内存

在这里插入图片描述

4、内存泄漏问题

在看了java中几种引用类型之后在看内存泄漏问题,什么是内存泄漏?就是指一部分对象一直占用内存,也清不掉,就好像这块内存空间没了,漏掉了。

其实在ThreadLocalMap的内部使用的是Entry类存储k-v

static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

        /**
         * The initial capacity -- MUST be a power of two.
         */
        private static final int INITIAL_CAPACITY = 16;
        
		//省略若干代码
}

可以看到Entry在实现时继承了弱引用,为什么这样呢?看注释是这样说的

The entries in this hash map extend WeakReference, using its main ref field as the key (which is always a ThreadLocal object). Note that null keys (i.e. entry.get() == null) mean that the key is no longer referenced, so the entry can be expunged from table. Such entries are referred to as "stale entries" in the code that follows.

Note that null keys (i.e. entry.get()* == null)
如果 key threadlocal 为 null 了,这个 entry 就可以清除了。
ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收 。

在ThreadLocal的使用时,线程的ThreadLocalMap中都存储了以ThreadLocal为key的值,如果ThreadLocal被清理了,那么
ThreadLocalMap中对应的数据会被清理吗?
并不会,原因是ThreadLocal被清理变成null之后,ThreadLocalMap被Thread所引用(强引用)并不会回收,只是key变成了null。

如何解决呢内存泄漏呢?最简单的办法就是用完之后remove,ThreadLocal不用了,就去ThreadLocalMap中清理到对用的k-v。

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

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

相关文章

干货!设备管理包括哪些内容?

本文将为您介绍&#xff1a;1、设备管理的内容有哪些2、好用的设备管理软件&#xff1b; 设备管理在企业管理中非常重要&#xff1a;设备是企业安全生产的前提&#xff0c;没有安全生产&#xff0c;一切工作都可能是无用功&#xff1b; 而好的设备管理可以极大地提高生产效率…

【数据库】MySQL数据库约束(六大约束)

目录 1.数据库约束 1.1约束类型 1.2 非空约束&#xff08;NOT NULL &#xff09; 1.3 唯一约束&#xff08;UNIQUE&#xff09; 1.4默认值约束&#xff08;DEFAULT &#xff09; 1.5主键约束&#xff08;PRIMARY KEY&#xff09; 1.6外键约束&#xff08;FOREIGN KEY &…

安全漏洞修复帖

对于项目漏洞都是一堆又不重要又很重要的事情一、修复HTTP 响应头缺失NginxTomcat响应头&#xff1a;值二、会话 Cookie 中缺少 secure 属性三、Html form 表单没有 CSRF 防护Security的示例Shiro的示例四、Host 头攻击NginxTomcatApache五、开启 options 方法一、修复HTTP 响应…

【设计模式】适配器模式和桥接模式

适配器模式 适配器模式 : 就是将一个类的接口变成客户端所期望的另一种接口&#xff0c;使得原本因为接口不匹配而无法一起工作的接口可以正常工作。属于结构型模式 比方说我有一个A牌子的奶瓶&#xff0c;然后买了个B牌子的奶嘴&#xff0c;不能匹配怎么办? 再买一个转换器…

Python3-输入和输出

Python3 输入和输出 输出格式美化 Python两种输出值的方式: 表达式语句和 print() 函数。 第三种方式是使用文件对象的 write() 方法&#xff0c;标准输出文件可以用 sys.stdout 引用。 如果你希望输出的形式更加多样&#xff0c;可以使用 str.format() 函数来格式化输出值。…

2020蓝桥杯真题洁净数 C语言/C++

题目描述 小明非常不喜欢数字 2&#xff0c;包括那些数位上包含数字 2 的数。如果一个数的数位不包含数字 2&#xff0c;小明将它称为洁净数。 请问在整数 1 至 n 中&#xff0c;洁净数有多少个&#xff1f; 输入描述 输入的第一行包含一个整数 n(1≤n≤10^6)。 输出描述 输…

VS Code 解决 SpringBoot 项目启动时报 Failed to refresh live data from process **** 的问题

问题 SpringBoot 启动后 &#xff0c;VS Code 报错 Failed to refresh live data from process ****。 现场是&#xff0c;SpringBoot 项目启动时&#xff0c;VS Code 将进行如下刷新&#xff0c;图片如下所示 当刷新 10 次以后&#xff0c;如果还是失败&#xff0c;则会抛出…

C++ queue的以及优先级队列(堆)的介绍和使用

因为队列与之前学习的栈等使用方法比较类似&#xff0c;所以对此进行简单介绍1 queue的介绍1. 队列是一种容器适配器&#xff0c;专门用于在FIFO上下文(先进先出)中操作&#xff0c;其中从容器一端插入元素&#xff0c;另一端提取元素。2. 队列作为容器适配器实现&#xff0c;容…

基于RK3588人工智能大算力5网口工业交换机

RK3588是瑞芯微旗下最新的8K旗舰SoC芯片&#xff0c;采用ARM架构&#xff0c;主要用于PC、个人移动互联网设备、边缘计算和其他数字多媒体应用。RK3588集成了四核Cortex-A76和四核Cortex-A55&#xff0c;以及单独的NEON协处理器&#xff0c;支持8K视频编解码。许多功能强大的嵌…

肠道菌群与健康:探究发酵食品、饮食方式、益生菌和后生元的影响

谷禾健康 肠道微生物群之间编织了一个复杂的相互作用网络&#xff0c;影响人体的营养吸收和代谢&#xff0c;免疫功能等&#xff0c;对我们的健康状态有很大的影响。 我们知道&#xff0c;肠道微生物群具有多样性&#xff0c;平衡性&#xff0c;稳定性等特征&#xff0c;但同时…

c语言面试题目整理

1、static有什么用途&#xff1f; 在C语言中&#xff0c;static主要定义全局静态变量&#xff0c;定义局部静态变量&#xff0c;定义静态函数 限制变量的作用域&#xff0c;设置变量的存储域。 static 关键字主要有两种作用&#xff1a; 第一&#xff0c;为某特定数据类型或对…

05 C语言数据类型

05 C语言数据类型 1、数据类型 编程语言对数据类型分为两派&#xff1a;一种认为要注重&#xff0c;一种认为可以忽视。 C语言类型 1、整数 : char < short < int < long < long long &#xff0c;bool 2、浮点数&#xff1a;float < double < long doub…

ChatGPT狂飙,文心一言会是李彦宏的“奇兵”吗?

“这是搜索领域新的一天。”前不久&#xff0c;微软CEO纳德拉在新版搜索引擎Bing的发布仪式上如此表示。Bing的技术支持&#xff0c;正是来自最近火遍全球的AI聊天机器人ChatGPT。仅仅两个月&#xff0c;OpenAI公司旗下的ChatGPT月活就超过1亿&#xff0c;成为史上增长最快的消…

Idea springboot springCloud热加载热调试常用的两种方式

场景描述 在项目开发的过程中&#xff0c;需要修改调试的时候偶每次都需要重启项目浪费时间&#xff0c;下面是我整理的两种常用的两种方式方式一 修改启动配置方式&#xff08;主要针对debug模式下&#xff09; 点击启动配置》edit configrations… configration下面修改Upd…

Win11+VS2019编译PCL1.12含gpu过程记录

之前直接用AllinOne安装了配置了一个pcl但是没有CUDA的支持&#xff0c;跑的速度很慢&#xff0c;现在想要集成到GPU上&#xff0c;需要重新编译一下PCL&#xff0c;之前自己编译一直有问题&#xff0c;而且还自己单独编译了相关的依赖库&#xff0c;结果发现只需要用AllinOne里…

《C++ Primer Plus》(第6版)第9章编程练习

《C Primer Plus》&#xff08;第6版&#xff09;第9章编程练习《C Primer Plus》&#xff08;第6版&#xff09;第9章编程练习1. 打印字符串2. 修改程序清单9.93. chaff4. sales《C Primer Plus》&#xff08;第6版&#xff09;第9章编程练习 1. 打印字符串 下面是一个头文件…

开源工具系列5:DependencyCheck

Dependency-Check 是 OWASP&#xff08;Open Web Application Security Project&#xff09;的一个实用开源程序&#xff0c;用于识别项目依赖项并检查是否存在任何已知的&#xff0c;公开披露的漏洞。 DependencyCheck 是什么 Dependency-Check 是 OWASP&#xff08;Open Web …

宿主机连接virtualbox中网络

背景&#xff1a;宿主机ubuntu&#xff0c;virtualbox中window中有VPN需要在宿主机也能共享到VPN网络。在virtualbox中添加host-only网络&#xff0c;取名vboxnet0这里在菜单栏中: 管理 -> 工具 -> network managervboxnet0在宿主的IP为192.168.56.1宿主机上运行ifconfig…

【Copula】基于二元Frank-Copula函数的风光出力场景生成方法【考虑风光出力的不确定性和相关性】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

JavaEE简单示例——依赖注入

简单介绍&#xff1a; 首先我们要知道&#xff0c;依赖注入就是赋值&#xff0c;带着这句话去理解依赖注入就非常的简单了&#xff0c;将文中所有的依赖注入全部脑部替换成给属性赋值&#xff0c;再去理解依赖注入的概念。 依赖注入&#xff08;DI&#xff09;是指IoC容器在运…