ThreadLocal的一些理解

news2024/11/18 16:48:27

阅读本篇博客您将了解如下内容:

  • TreadLocal的作用。
  • ThreadLocal的实现原理。
  • ThreadLocal是否会引起内存泄漏,在什么样的条件下引发,如何避免。

1、ThreadLocal的作用


使用线程封闭的指导思想来解决变量共享的并发安全问题,–可以简单理解为各玩各的雨女无瓜

2、ThreadLocal 源码初探

2.1 ThreadLocal的set方法的代码如下:

 // 获取当前线程对象
     Thread t = Thread.currentThread();
     // 根据线程对象获取ThreadLocalMap对象(ThreadLocalMap被Thread持有 threadLocals )
     ThreadLocalMap map = getMap(t);
     // 如果ThreadLocalMap存在,则直接插入;不存在,则新建ThreadLocalMap
     if (map != null){
         map.set(this, value);
         }else{
         createMap(t, value);
     }

threadLocals : 线程Thread 对象内部属性,这个属性默认就是null,由ThreadLocal.set 进行初始化。如果已经初始化了,则使用当前ThreadLocal的实例作为key,待存值作为value保存到当前线程的ThreadLocalMap变量中。
其实这里以前有个问题一直困扰我,为什么不直接给Thread的hreadLocals属性设置成object类型,然后set值的时候直接赋值给 threadLocals。
后面明白了一个Thread加一个ThreadLocal 只能保存一个val,但是一个Thread 可以对应多个ThreadLocal,一个线程对象属性可能被多个ThreadLocal共同持有。

 		ThreadLocal<String> th1 = new ThreadLocal<>();
        ThreadLocal<String> th2 = new ThreadLocal<>();
        th1.set("123");
        th2.set("456");
        System.out.println(th1.get());
        System.out.println(th2.get());

在这里插入图片描述

2.2 ThreadLocalMap是ThreadLocal 一个内部类,其本质和Map没有任何关系,也没有实现Map接口。内部使用Entry(类似Map key-value 对象) 数组存储数据,使用Hash 算法计算下标。

 static class ThreadLocalMap {

        /**
         *  使用弱引用包装ThreadLocal 作为map Key 
         *  在某些情况下key会被回收掉
         */
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

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

     
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
           //使用hashCode 计算下标
          //这里使用hashCode 跟我们普通对象不一样,通过自增长计算出来
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

2.3 ThreadLocalMap的set方法,在出现hash冲突时,只是将下标向后移动,找到空闲的位置。正如set 方法注释上写,set 并不支持快速set、冲突了通过向后遍历数组找到空位置。

 private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
          // 循环里面处理hash冲突情况
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                ThreadLocal<?> k = e.get();

                if (k == key) { //相等直接覆盖
                    e.value = value;
                    return;
                }

                if (k == null) { 
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
            // 当hash 冲突时,会一直向后找,直到有空位置
            tab[i] = new Entry(key, value);
            int sz = ++size;
            //在位置i 后面搜寻是否有key 回收情况,则删除数组位置,返回true 
            // 当有删除,不需要判断扩容情况了,一个新增对应删除,容量都没有增加
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash(); //扩容数组
        }

2.4 ThreadLocalMap的get方法会通过向后遍历匹配出来,这个以HashMap 相比差距挺大的。插入、查找效率都在N之间。

  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);
        }

        //向后查找符合要去key
        private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
            Entry[] tab = table;
            int len = tab.length;

            while (e != null) { //遇到null 就停下来
                ThreadLocal<?> k = e.get();
                if (k == key)
                    return e;
                if (k == null)
                     // k 已经被gc 了
                    //在数组中删除这个位置,这样可以帮助value 回收了
                    expungeStaleEntry(i);
                else
                    i = nextIndex(i, len);
                e = tab[i];
            }
            return null;
        }

3、ThreadLocal会造成内存泄漏吗?

答案是会的。
1、当线程栈持有ThreadLocal,作为Entry key的它不会被gc,当ThreadLocalRef 引用失效时(线程生命周期结束),ThreadLocal 就会在下次gc时被回收掉。
但是目前咱们开发过程中都是使用池化的思想即使用线程池进而达到线程复用,所以线程并未结束,这种情况下ThreadLocalRef 就不会失效,但是它又作为ThreadLocalMap中Entry 的key,它不会被gc,导致内存泄漏,所以需要手动remove。

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

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

相关文章

跳表是一种什么样的数据结构

跳表是有序集合的底层数据结构&#xff0c;它其实是链表的一种进化体。正常链表是一个接着一个用指针连起来的&#xff0c;但这样查找效率低只有O(n)&#xff0c;为了解决这个问题&#xff0c;提出了跳表&#xff0c;实际上就是增加了高级索引。朴素的跳表指针是单向的并且元素…

FPGA之复选器(2)

8:1 复选器 每个slice具有一个F7AMUX和一个F7BMUX F7AMUX 和 F7BMUX 原语&#xff1a; MUXF7 MUXF7_inst ( .O(O),//Output of MUX to general routing. I0(10), // Input (tie to LUT6 O6 pin) II(II), //Input (tie to LUT6 O6 pin) SS)// Input select to MUX 这两个…

【网站项目】167校园失物招领小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Linux之用户和用户组的深入了解

目录 一、简介 1.1、用户&#xff1a; 1.2、用户组 1.3、UID和GID 1.3、用户账户分类 查看用户类别 超级用户root(0) 程序用户(1~499) 普通用户(500~65535) 二、用户 2.1、添加新的用户账号&#xff1a;useradd 2.2、删除账号&#xff1a;userdel 有-r与没有-r区别…

人为物累,心为形役

一、人是什么 你是你&#xff0c;他是他&#xff0c;我是我&#xff0c;有什么区别吗&#xff0c;直到自我发现我与你不同时&#xff0c;不同是什么&#xff0c;身体结构&#xff1f;人生经历&#xff1f;所拥有的一切&#xff1f;暂时搁置这些的话&#xff0c;抽离我们的意识…

【关于深度学习的一些资料】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 动手学深度学习Awesome Deep LearningTensorFlow Official ModelsPyTorch Image ModelsDeep Reinforcement LearningNeural Style Transfer 动手学深度学习 动手学深度学习 https://zh.d2l.ai/chapter_installation/index.…

4核8G服务器腾讯云和阿里云租用价格对比,2024更新

4核8G云服务器多少钱一年&#xff1f;阿里云ECS服务器u1价格955.58元一年&#xff0c;腾讯云轻量4核8G12M带宽价格是646元15个月&#xff0c;阿腾云atengyun.com整理4核8G云服务器价格表&#xff0c;包括一年费用和1个月收费明细&#xff1a; 云服务器4核8G配置收费价格 阿里…

学习大数据所需的java基础(5)

文章目录 集合框架Collection接口迭代器迭代器基本使用迭代器底层原理并发修改异常 数据结构栈队列数组链表 List接口底层源码分析 LinkList集合LinkedList底层成员解释说明LinkedList中get方法的源码分析LinkedList中add方法的源码分析 增强for增强for的介绍以及基本使用发2.使…

SpringIOC之support模块StaticApplicationContext

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…

【Redis】理论进阶篇------浅谈Redis的缓存穿透和雪崩原理

一、缓存穿透 1、概念 缓存穿透&#xff08;查不到数据&#xff09;&#xff0c;是指当用户想要查询数据的时候&#xff0c;会先去Redis中取命中&#xff0c;如果Redis中没有该数据&#xff0c;那么就会向数据库中去查找数据。如果数据库中也没有&#xff0c;则该次查询结果失…

Google插件Sider: ChatGPT Sidebar + GPTs GPT-4 Turbo Sider

Sider: ChatGPT Sidebar 可以使得满屏都是机器人&#xff0c;左侧栏可以打开访问GPT-4. 配置跳板机地址 google 搜索的右侧也有打开

Docker基础篇(-)

docker 三个要素 镜像容器仓库 CentOS 6.8 安装 docker centos 7.0 yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager -y --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo systemctl start docker 启动Docker&…

Mouse IFN-α ELISA kit (Quick Test)

干扰素α&#xff08;IFN-α&#xff09;是一类由免疫细胞分泌的内源性调节因子&#xff0c;也被称为白细胞干扰素&#xff0c;主要参与响应病毒感染的先天性免疫。 基于结构特征、受体、细胞来源和生物活性的不同&#xff0c;干扰素可被分为Ⅰ、Ⅱ、Ⅲ三种类型&#xff0c;其中…

Qt Creator在#include第三方库不带.h后缀的文件时,没有智能提示和自动补全

1、问题截图 OSG文件目录下有很多头文件&#xff08;均不带.h后缀&#xff09;&#xff0c;Qt Creator可以识别到OSG目录&#xff0c;但是OSG目录下的所有头文件识别不到 2、原因 找到原因是因为Qt Creator开启了ClanCodeModel插件导致的 3、解决方法 1、在Qt Creator中…

Apache服务

目录 引言 一、常见的http服务程序 &#xff08;一&#xff09;lls &#xff08;二&#xff09;nginx &#xff08;三&#xff09;Apache &#xff08;四&#xff09;Tomcat 二、Apache特点 三、Apache服务的安装 &#xff08;一&#xff09;yum安装及配置文件 1.配置…

机器学习基本概念(李宏毅课程)

目录 一、概念:1、机器学习概念:2、深度学习概念&#xff1a; 二、深度学习中f(.)的输入和输出&#xff1a;1、输入&#xff1a;2、输出&#xff1a; 三、三种机器学习任务&#xff1a;1、Regression回归任务介绍&#xff1a;2、Classification分类任务介绍&#xff1a;3、Stru…

MySql-DML-修改数据update

目录 修改数据 修改数据 update语法&#xff1a; update 表名 set 字段名1 值1 , 字段名2 值2 , .... [where 条件] ;案例1&#xff1a;将tb_emp表中id为1的员工&#xff0c;姓名name字段更新为’张三’ update tb_emp set name张三,update_timenow() where id1;案例2&…

CogFixtureTool(坐标系、校正与定位)

坐标系 任何VisionPro图像都支持一组坐标空间&#xff0c;为表达特定特征的位置提供数字框架。最有用的空间是根空间和用户空间&#xff0c;根空间将点与原始获取图像中的像素相关联&#xff0c;用户空间用于获得校准和固定空间中的特征位置和测量值。 根空间 图像的根空间…

python统计分析——使用AIC进行模型选择

参考资料&#xff1a;用python动手学统计学 1、导入库 # 导入库 # 用于数值计算的库 import numpy as np import pandas as pd import scipy as sp from scipy import stats # 用于绘图的库 import matplotlib.pyplot as plt import seaborn as sns sns.set() # 用于估计统计…

C++医学临床影像信息管理系统源码

一、医学影像信息系统概述 在现代医疗行业中&#xff0c;医学影像信息系统包括RIS&#xff0c;按照DICOM3.0国际标准设计&#xff0c;是由高性能服务器、网络和存储设备组成的硬件支撑平台&#xff0c;以大型关系数据库作为数据和影像的存储和管理工具&#xff0c;以医学影像的…