ThreadLocal的一些理解

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

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

1、ThreadLocal的作用


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

2、ThreadLocal 源码初探

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

 // 获取当前线程对象Thread t = Thread.currentThread();// 根据线程对象获取ThreadLocalMap对象(ThreadLocalMap被Thread持有 threadLocals )ThreadLocalMap map = getMap(t);// 如果ThreadLocalMap存在,则直接插入;不存在,则新建ThreadLocalMapif (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;elsereturn getEntryAfterMiss(key, i, e);}//向后查找符合要去keyprivate 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);elsei = 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.rhkb.cn/news/261844.html

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

相关文章

【数据库】达梦数据库DM8开发版安装

目录 一、达梦数据库概述 1.1 达梦数据库简介 1.2 产品特性 1.3 产品架构 二、安装前准备 2.1 新建 dmdba 用户 2.2 修改文件打开最大数 2.3 挂载镜像 2.4 新建安装目录 2.5 修改安装目录权限 三、数据库安装 3.1 命令行安装 3.2 配置环境变量 四、配置实例 4.1…

十六、多边形填充和绘制

项目功能实现&#xff1a;对多边形进行轮廓绘制和填充 按照之前的博文结构来&#xff0c;这里就不在赘述了 一、头文件 mult-drawing.h #pragma once#include<opencv2/opencv.hpp>using namespace cv;class Mult_Drawing { public:void mult_drawing(); };#pragma onc…

Rman全备和增量备份说明

RMAN备份分为全备和增量备份&#xff0c;全备不能成为增量备份策略的一部分&#xff0c;它也不能作为后续增量备份的基础。 RMAN增量备份分为0、1、2三级&#xff0c;其中0级备份是增量备份的基础&#xff0c;备份内容也跟全备份一样&#xff0c;要使用增量备份&#xff0c;必…

Android 面试问题 2024 版(其二)

Android 面试问题 2024 版&#xff08;其二&#xff09; 六、多线程和并发七、性能优化八、测试九、安全十、Material设计和 **UX/UI** 六、多线程和并发 Android 中的进程和线程有什么区别&#xff1f; 答&#xff1a;进程是在自己的内存空间中运行的应用程序的单独实例&…

[计算机网络]---TCP协议

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一 、TCP协…

基于SpringBoot + Layui的社区物业管理系统

项目介绍 社区物业管理系统是基于java编程语言&#xff0c;springboot框架&#xff0c;idea工具&#xff0c;mysql数据库进行开发&#xff0c;本系统分为业主和管理员两个角色&#xff0c;业主可以登陆系统&#xff0c;查看车位费用信息&#xff0c;查看物业费用信息&#xff0…

代码随想录算法训练营第58天 | 392.判断子序列 115.不同的子序列

判断子序列 这道题可以双指针方法解决。 class Solution { public:bool isSubsequence(string s, string t) {int s_index 0;for(int t_index 0; t_index < t.size(); t_index) {if(s[s_index] t[t_index]) {s_index;}}return s_index s.size();} };用动态规划也是可解…

力扣OJ题——随机链表的复制

题目&#xff1a; 138. 随机链表的复制 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 要求&#xff1a;构造这个链表的 深拷贝 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中…

Vue状态管理库-Pinia

一、Pinia是什么&#xff1f; Pinia 是 Vue 的专属状态管理库&#xff0c;它允许支持跨组件或页面共享状态&#xff0c;即共享数据&#xff0c;他的初始设计目的是设计一个支持组合式API的 Vue 状态管理库&#xff08;因为vue3一个很大的改变就是组合式API&#xff09;,当然这…

简介高效的 CV 入门指南: 100 行实现 InceptionResNet 图像分类

简介高效的 CV 入门指南: 100 行实现 InceptionResNet 图像分类 概述InceptionResNetInception 网络基本原理关键特征 ResNet 网络深度学习早期问题残差学习 InceptionResNet 网络InceptionResNet v1InceptionResNet v2改进的 Inception 模块更有效的残差连接设计 100 行实现 I…

Docker本地部署Rss订阅工具并实现公网远程访问

文章目录 1. Docker 安装2. Docker 部署Rsshub3. 本地访问Rsshub4. Linux安装Cpolar5. 配置公网地址6. 远程访问Rsshub7. 固定Cpolar公网地址8. 固定地址访问 Rsshub是一个开源、简单易用、易于扩展的RSS生成器&#xff0c;它可以为各种内容生成RSS订阅源。 Rsshub借助于开源社…

JDBC简介

JDBC体系结构 Java Data Base Connectivity&#xff08;JDBC&#xff09;&#xff0c;Java数据库连接。 JDBC重要的类和接口 JDBC API 定义了一组用于与数据库进行通信的接口和类&#xff0c;这些接口和类都定义在Java.sql包中。 类或接口作用DriverManager处理驱动程序的加…

校园兼职|大学生校园兼职小程序|基于微信小程序的大学生校园兼职系统设计与实现(源码+数据库+文档)

大学生校园兼职小程序目录 目录 基于微信小程序的大学生校园兼职系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户​微信端功能模块​ 2、管理员服务端功能模块 &#xff08;1&#xff09; 兼职管理 &#xff08;2&#xff09;论坛管理 &#xff08;3&…

Leetcode 102.二叉树的层序遍历

目录 题目 思路 题目 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;[[3],[9,20],[15,7]]示例…

leetcode hot100组合综合四

本题中&#xff0c;是要求nums中求的总和为target的排列数&#xff0c;因为题中说了&#xff0c;元素顺序不同&#xff0c;则可以视为不同的结果之一。 所以&#xff0c;根据对背包问题的总结&#xff0c;本题中元素可以重复使用&#xff0c;是完全背包并且需要求排列数&#…

Redis之缓存穿透问题解决方案实践SpringBoot3+Docker

文章目录 一、介绍二、方案介绍三、Redis Docker部署四、SpringBoot3 Base代码1. 依赖配置2. 基本代码 五、缓存优化代码1. 校验机制2. 布隆过滤器3. 逻辑优化 一、介绍 当一种请求&#xff0c;总是能越过缓存&#xff0c;调用数据库&#xff0c;就是缓存穿透。 比如当请求一…

数字之美:探索人工智能绘画的奇妙世界

目录 引言AI绘画的定义与发展历程定义与发展历程AI绘画产品有哪些? AI绘画的应用领域设计与创意产业影视与游戏制作数字艺术与展览 AI绘画的基本原理与技术深度学习与神经网络生成对抗网络&#xff08;GAN&#xff09;风格迁移算法 AI绘画效果展示一只带着墨镜的小猫在高楼林立…

05.STLvector、list、stack、queue

STL标准模板库 standard template library STL将原来常用的容器和操作进行封装&#xff0c;增加了C的编码效率 容器 string #include vector #include list #include stack #include queue #include set #include map #include 迭代器 容器和算法之间的粘合剂&#xff0…

【ACM出版】第五届计算机信息和大数据应用国际学术会议(CIBDA 2024)

第五届计算机信息和大数据应用国际学术会议&#xff08;CIBDA 2024&#xff09; 2024 5th International Conference on Computer Information and Big Data Applications 重要信息 大会官网&#xff1a;www.ic-cibda.org 大会时间&#xff1a;2024年3月22-24日 大会地点&#…

【JavaEE】_form表单构造HTTP请求

目录 1. form表单的格式 1.1 form表单的常用属性 1.2 form表单的常用搭配标签&#xff1a;input 2. form表单构造GET请求实例 3. form表单构造POST请求实例 4. form表单构造法的缺陷 对于客户端浏览器&#xff0c;以下操作即构造了HTTP请求&#xff1a; 1. 直接在浏览器…