【Java并发知识总结 | 第九篇】ThreadLocal总结

news2024/11/25 15:35:09

在这里插入图片描述

文章目录

  • 9.ThreadLocal总结
    • 9.1ThreadLocal是什么?
    • 9.2ThreadLocal的作用?
    • 9.3使用ThreadLocal
    • 9.4ThreadLocal原理
    • 9.5ThreadLocal问题:内存泄漏/溢出
    • 9.6为什么key要设计成弱引用?
    • 9.7ThreadLocal中的强弱引用关系
    • 9.8ThreadLocalMap怎么解决Hash冲突?

9.ThreadLocal总结

9.1ThreadLocal是什么?

  • ThreadLocal是 Java 中提供的一种用于实现线程局部变量的工具类。
  • 它允许每个线程都拥有自己的独立副本,从而实现线程隔离,用于解决多线程中共享对象的线程安全问题

9.2ThreadLocal的作用?

  • 作为数据副本,当某些数据是以线程为作用域并且不同线程有不同数据副本时,可以考虑 ThreadLocal。
  • 保存线程上下文信息,在任意需要的地方可以获取,避免显示传参。
  • 解决线程安全问题,避免某些情况需要考虑线程安全必须同步带来的性能损失。

9.3使用ThreadLocal

  • 创建ThreadLocal
  • 设置ThreadLocal的值
  • 获取 ThreadLocal 的值
  • 删除 ThreadLocal 的值
//创建一个ThreadLocal变量
public static ThreadLocal<String> localVariable = new ThreadLocal<>();

//设置ThreadLocal变量的值
localVariable.set("当前用户值");

//获取ThreadLocal变量的值
String value = localVariable.get();

//删除ThreadLocal变量的值
localVariable.remove();
  • 下图为使用ThreadLocal保存登录用户信息

image-20240428205326426

9.4ThreadLocal原理

  1. ThreadLocal 本身并不存储任何值,它只是作为一个映射,来映射线程的局部变量。
  2. 当一个线程调用 ThreadLocal 的 set 或 get 方法时,实际上是访问线程自己的 ThreadLocal.ThreadLocalMap
  3. ThreadLocalMap 是 ThreadLocal 的静态内部类,它内部维护了一个 Entry 数组,key 是 ThreadLocal 对象,value 是线程的局部变量本身

1、调用set()和get()的过程?

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
  • 调用 ThreadLocal.set()
    • 调用 getMap(Thread) --> 返回当前线程的 ThreadLocalMap < ThreadLocal, value >
    • map.set(this, value),this 是 ThreadLocal,value 为想要实现线程隔离的对象
  • 调用 get()
    • 调用getMap(Thread) --> 返回当前线程的 ThreadLocalMap<ThreadLocal, value>-->map.getEntry(this)
    • 返回value

2、ThreadLocal 的实现原理?

  • 实现原理 :每个线程维护一个 Mapkey 为 ThreadLocal 对象(当前对象),value 为想要实现线程隔离的对象

    • 当需要存线程隔离的对象时,通过 ThreadLocal 的 set 方法将对象存入 Map 中
    • 当需要取线程隔离的对象时,通过 ThreadLocal 的 get 方法从 Map 中取出对象。
    • Map 的大小由 ThreadLocal 对象的多少决定

    image-20240428210254618

9.5ThreadLocal问题:内存泄漏/溢出

  1. 通常情况下,随着线程 Thread 的结束,其内部的 ThreadLocalMap 也会被回收,从而避免了内存泄漏。
  2. 如果一个线程一直在运行,并且其 ThreadLocalMap 中的 Entry.value 一直指向某个强引用对象那么这个对象就不会被回收,从而导致内存泄漏
  3. 当 Entry 非常多时,可能就会引发更严重的内存溢出问题。

解决方案:

很简单,使用完 ThreadLocal 后,及时调用 remove() 方法释放内存空间。

try {
    
    threadLocal.set(value);
    // 执行业务操作
    
} finally {
    threadLocal.remove(); // 确保能够执行清理
}

remove() 方法会将当前线程的 ThreadLocalMap 中的所有 key 为 null 的 Entry 全部清除,这样就能避免内存泄漏问题。

private void remove(ThreadLocal<?> key) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
            e != null;
            e = tab[i = nextIndex(i, len)]) {
        if (e.get() == key) {
            e.clear();
            expungeStaleEntry(i);
            return;
        }
    }
}

public void clear() {
    this.referent = null;
}

9.6为什么key要设计成弱引用?

弱引用的好处是,当内存不足的时候,JVM 会主动回收掉弱引用的对象。

  • 例子:

    WeakReference key = new WeakReference(new ThreadLocal());
    
  • key 是弱引用,new WeakReference(new ThreadLocal()) 是弱引用对象,当 JVM 进行垃圾回收时,如果发现了弱引用对象,就会将其回收

  • 一旦 key 被回收,ThreadLocalMap 在进行 set、get 的时候就会对 key 为 null 的 Entry 进行清理。

总结:在 ThreadLocal 被垃圾收集后,下一次访问 ThreadLocalMap 时,Java 会自动清理那些键为 null 的条目get(), set(), remove())等方法均会触发)

9.7ThreadLocal中的强弱引用关系

  • 强引用:new User(“张三”)

  • 弱引用:

    • userThreadLocal 是一个强引用,new ThreadLocal<>() 是一个强引用对象;
    • new User("张三") 是一个强引用对象。
    • 在 ThreadLocalMap 中,key = new ThreadLocal<>()是一个弱引用对象
      • 当 JVM 进行垃圾回收时,如果发现了弱引用对象,就会将其回收。
    ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
    userThreadLocal.set(new User("张三"));
    

关系链:

  • ThreadLocal 强引用 -> ThreadLocal 对象。
  • 当前的Thread 强引用 -> ThreadLocalMap。
  • ThreadLocalMap[i] 强引用了 -> Entry。
  • Entry.key 弱引用 -> ThreadLocal 对象。(当执行remove等方法,垃圾回收器会回收key为null的键值对)
  • Entry.value 强引用 -> 线程的局部变量对象。

image-20240428214208438

引用类型被垃圾回收时间用途死亡时间
强引用从来不会对象的一般状态将强引用弱化,并gc后
软引用内存不足对象缓存内存不足,并gc后
弱引用垃圾回收对象缓存gc后
虚引用未知未知未知

9.8ThreadLocalMap怎么解决Hash冲突?

  1. ThreadLocalMap 没有使用链表,自然也不是用链地址法来解决冲突了
  2. 它用的是另外一种方式——开放定址法。开放定址法是什么意思呢?简单来说,就是这个坑被人占了,那就接着去找空着的坑。

在这里插入图片描述

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

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

相关文章

web安全---CSRF漏洞/OWASP-CSRFTester的使用

what 跨站请求伪造 Cross Site Request Forgery how 攻击者诱骗点击恶意网页&#xff0c;盗用&#xff08;伪造&#xff09;受害者的身份&#xff0c;以受害者的名义向服务器发送恶意请求,而这种恶意请求在服务端看起来是正常请求 CSRF&&XSS区别 他们最本质区别就…

利用RunnerGo数据大屏强化测试管理与决策

测试平台中的数据大屏在提供实时监控、统计分析、效率提升、制定策略和促进沟通等方面具有重要的意义。它为测试团队提供更全面、更直观的数据支持&#xff0c;有助于提高测试质量和效率&#xff0c;减少风险&#xff0c;并加强团队协作和沟通。 数据大屏也是RunnerGo的核心特…

21 Debian如何配置Apache2(1)配置文件摊开看

作者&#xff1a;网络傅老师 特别提示&#xff1a;未经作者允许&#xff0c;不得转载任何内容。违者必究&#xff01; Debian如何配置DNS服务&#xff08;2&#xff09;主从服务器 《傅老师Debian小知识库系列之20》——原创 前言 傅老师Debian小知识库特点&#xff1a; 1、…

LLM学习笔记-4

从Hugging Face加载预训练权重 因为每次训练都要有资源消耗 (GPU算力&#xff0c;还有时间成本&#xff09;&#xff0c;所以说及时保存模型是非常重要的。教大家如何去下载Hugging Face的模型进行生成文本 pip install transformers pip install tiktokenfrom importlib.me…

【树莓派】yolov5 Lite,目标检测,行人检测入侵报警,摄像头绑定

延续之前的程序&#xff1a; https://qq742971636.blog.csdn.net/article/details/138172400 文章目录 播放声音pygame不出声音怎么办&#xff08;调节音量&#xff09;树莓派上的音乐播放器&#xff08;可选&#xff09;命令行直接放歌&#xff08;尝试放mp3歌曲&#xff09; …

用vue3实现留言板功能

效果图&#xff1a; 代码&#xff1a; <script setup lang"ts"> import { ref } from vue;interface Message {name: string;phone: string;message: string; }const name ref<string>(); const phone ref<string>(); const message ref<st…

【SQL】❤️数据库理论加实践详细教程❤️实践出真知❤️

SQL(结构化查询语言) 基础部分 SQL作用 按照作用划分可以划分为四个模块&#xff0c;从而由此行文 DDL&#xff08;数据定义语言&#xff09;: DDL涉及的命令允许用户定义或修改数据库的结构。主要命令包括&#xff1a; CREATE&#xff1a;用于创建新的数据库对象&#xff0c;…

使用 BurpSuite 基于 Token 机制实施暴力破解

前言 Token是一种用于身份验证和授权的令牌&#xff0c;通常由服务器生成并发送给客户端&#xff0c;客户端在后续的请求中携带该令牌来进行身份验证和授权操作。Token的使用可以增强应用程序的安全性&#xff0c;避免了直接传递敏感凭证&#xff08;如用户名和密码&#xff0…

SpringMVC整体工作流程

. 用户发起一个请求&#xff0c;请求首先到达前端控制器前端控制器接收到请求后会调用处理器映射器&#xff0c;由此得知&#xff0c;这个请求该由哪一个Controller来进行处理(并未调用Controller)&#xff1b;前端控制器调用处理器适配器&#xff0c;告诉处理器适配器应该要…

2024抖音AI图文带货班:在这个赛道上 乘风破浪 拿到好效果

课程目录 1-1.1 AI图文学习指南 1.mp4 2-1.2 图文带货的新机会 1.mp4 3-1.3 2024年优质图文新标准 1.mp4 4-1.4 图文如何避免违规 1.mp4 5-1.5 优质图文模板解析 1.mp4 6-2.1 老号重启 快速破局 1.mp4 7-2.2 新号起号 不走弯路 1.mp4 8-2.3 找准对标 弯道超车 1.mp4 9…

判断前端入参是否空否则提示前端写法

vue2中 前端先声明一个变量&#xff0c;用于alert判断 在templeat中定义一个提示语句 然后在点击事件时判断一下是否展示

【Python 对接QQ的接口】简单用接口查询【等级/昵称/头像/Q龄/当天在线时长/下一个等级升级需多少天】

文章日期&#xff1a;2024.04.28 使用工具&#xff1a;Python 类型&#xff1a;QQ接口 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 AES解密处理&#xff08;直接解密即可&#xff09;&#xff08;crypto-js.js 标准算法&#xff09;&…

<计算机网络自顶向下> Internet Protocol

互联网中的网络层 IP数据报格式 ver: 四个比特的版本号&#xff08;IPV4 0100, IPV6 0110&#xff09; headlen&#xff1a;head的长度&#xff08;头部长度字段&#xff08;IHL&#xff09;指定了头部的长度&#xff0c;以32位字&#xff08;4字节&#xff09;为单位计算。这…

How to solve matplotlib Chinese garbled characters in Ubuntu 22.04

conda create -n huizhou python3.8conda activate huizhouconda install numpy matplotlibpip install mplfontsmplfonts init# 导入必要的库 import numpy as np import matplotlib.pyplot as plt# 创建角度数组&#xff0c;从0到2π x np.linspace(0, 2 * np.pi, 100)# 计算…

NFTScan | 04.22~04.28 NFT 市场热点汇总

欢迎来到由 NFT 基础设施 NFTScan 出品的 NFT 生态热点事件每周汇总。 周期&#xff1a;2024.04.22~ 2024.04.28 NFT Hot News 01/ ApeCoin DAO 发起「由 APE 代币支持的 NFT Launchpad」提案投票 4 月 22 日&#xff0c;ApeCoin DAO 社区发起「由 APE 代币支持的 NFT Launch…

第8章 软件工程

一、软件工程概述 &#xff08;一&#xff09;软件危机 1、含义&#xff1a;落后的软件生产方式无法满足迅速增长的计算机软件需求&#xff0c;从而导致软件开发与维护过程中出现一系列严重问题的现象。 2、解决方案&#xff1a;引入软件工程的思想。 &#xff08;二&#x…

ubuntu samba 安装与配置

ubuntu samba 安装与配置 一&#xff1a;安装二&#xff1a;添加samba访问账号及密码三&#xff1a;修改配置文件四&#xff1a;重启服务五&#xff1a;登录 一&#xff1a;安装 sudo apt update sudo apt install samba samba-common二&#xff1a;添加samba访问账号及密码 …

YOLOv8: 快速而准确的对象检测

YOLOv8: 快速而准确的对象检测 背景 对象检测是计算机视觉中的一个关键任务,它可以帮助我们在图像或视频中识别和定位感兴趣的物体。其中,YOLO(You Only Look Once)系列是一类非常出色的实时对象检测算法,以其快速和准确的特点而闻名。YOLOv8是YOLO系列的最新版本,由Ultralyti…

Linux下的基本指令(1)

嗨喽大家好呀&#xff01;今天阿鑫给大家带来Linux下的基本指令&#xff08;1&#xff09;&#xff0c;下面让我们一起进入Linux的学习吧&#xff01; Linux下的基本指令 ls 指令pwd命令cd 指令touch指令mkdir指令(重要)rmdir指令 && rm 指令(重要)man指令(重要)cp指…

020Node.js的FS模块使用fs.mkdir创建目录

Node.js的FS模块使用fs.mkdir创建目录 //fs.mkdir 创建目录 /*path 将创建的目录路径mode 目录权限&#xff08;读写权限&#xff09;&#xff0c;默认777callback 回调&#xff0c;传递异常参数err*/ const fsrequire(fs);fs.mkdir(./css,(err)>{if(err){console.log(err)…