【SpringBoot】ThreadLocal 的详解

一、ThreadLocal 简介

ThreadLocal 叫做线程变量,意思是 ThreadLocal 中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量。ThreadLocal 为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

ThreadLocal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。这里有几点需要注意:

  • 因为每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这也是 ThreadLocal 命名的由来。
  • 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。

ThreadLocal 提供了线程本地的实例。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的实例副本。

ThreadLocal 变量通常被 private static 修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

下图可以增强理解:

 二、ThreadLocal 与 Synchronized 的区别

ThreadLocal<T> 其实是与线程绑定的一个变量。ThreadLocal 和 Synchorized 都用于解决多线程并发访问。

但是 ThreadLocal 和 Synchorized 有本质的区别:

  1. Synchorized 用于线程间的数据共享,而 ThreadLocal 则用于线程间的数据隔离。
  2. Synchorized 是利用锁的机制,使变量或代码块在某一时刻只能被一个线程访问。而 ThreadLocal 为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享。

而 Synchorized 却正好相反,它用于在多个线程间通信时能够获得数据共享。

总结:

一句话理解 ThreadLocal ,threadLocal 是作为当前线程中属性 ThreadLocalMap 集合中的某一个 Entry 的 key 值,Entry(threadlocal,value),虽然不同的线程之间 threadLocal 这个 key 值是一样,但是不同的线程所拥有的 ThreadLocalMap 是独一无二的,也就是不同的线程间同一个 ThreadLocal(key)对应存储的值(value)不一样,从而到达了线程间变量隔离的目的,但是在同一个线程中这个 value 变量地址是一样的。

三、ThreadLocal 的简单使用

public class ThreadLocalTest {private static ThreadLocal<String> localVar = new ThreadLocal<String>();static void print(String str) {// 打印当前线程中本地内存中变量的值System.out.println(str + ":" + localVar.get());// 清除内存中的本地变量localVar.remove();}public static void main(String[] args) throws InterruptedException {new Thread(new Runnable() {@Overridepublic void run() {ThreadLocalTest.localVar.set("xdclass_A");print("A");// 打印本地变量System.out.println("清楚后:" + localVar.get());}}, "A").start();Thread.sleep(1000);new Thread(new Runnable() {@Overridepublic void run() {ThreadLocalTest.localVar.set("xdclass_B");print("B");// 打印本地变量System.out.println("清楚后:" + localVar.get());}}, "B").start();}
}A:xdclass_A
清楚后:null
B:xdclass_B
清楚后:null

从这个示例中我们可以看到,两个线程分别获取了自己线程存放的变量,他们之间变量的获取并不会错乱。这个的理解也可以结合图,相信会有一个更深刻地理解。

四、ThreadLocal 常见使用场景

ThreadLocal 适用于如下两种场景

  1. 每个线程需要有自己单独的实例
  2. 实例需要在多个方法中共享,但不希望被多线程共享

对于第一点,每个线程拥有自己实例,实现它的方式很多。例如可以在线程内部构建一个单独的实例。ThreadLoca 可以以非常方便的形式满足该需求。

对于第二点,可以在满足第一点(每个线程有自己的实例)的条件下,通过方法间引用传递的形式实现。ThreadLocal 使得代码耦合度更低,且实现更优雅。

存储用户 userInfo 场景:
@Slf4j
public class OnlineUserUtil {private final static ThreadLocal<UserInfo> threadLocal = new ThreadLocal<>();public static void set(UserInfo userInfo) {threadLocal.set(userInfo);}public static UserInfo get() {return threadLocal.get();}public static void remove() {threadLocal.remove();}}
@Slf4j
@Aspect
@Component
@Order(2)
public class TokenAuthenticationAspect {@Before(value = "@annotation(tokenAuthentication)")public void doBefore(JoinPoint pjp, TokenAuthentication tokenAuthentication) {// 校验代码log.info("验证成功,保存到threadLocal userInfo={}", userInfo);OnlineUserUtil.set(userInfo);}@AfterReturning(value = "@annotation(tokenAuthentication)")public void doAfter(TokenAuthentication tokenAuthentication) {OnlineUserUtil.remove();}}

这样在方法中,都能用到 userInfo 这个对象。

五、如何正确的使用 ThreadLocal

  1. 将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。
  2. 每次使用完ThreadLocal,都调用它的remove()方法,清除数据。

六、参考文档

  • 史上最全ThreadLocal 详解(一)

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

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

相关文章

虚拟机centos设置网络模式(桥接|NAT)

前言 桥接模式是通过物理网卡直接与外部网络建立联系的&#xff0c;而NAT模式则是通过虚拟网卡VMnet1或VMnet8通过宿主机共享IP与外部建立网络关系当需要将虚拟机资源共享给局域网用户使用时&#xff0c;宜采用桥接模式&#xff1b;当需要保护虚拟机资源&#xff0c;确保只能由…

封面从这里取好啦

文章目录 前端NPMViteNode.js 后端JavaMavenPython 数据库算法 前端 NPM Vite Node.js 后端 Java Maven Python 数据库 算法

C#使用whisper.net实现语音识别(语音转文本)

目录 介绍 效果 输出信息 项目 代码 下载 介绍 github地址&#xff1a;https://github.com/sandrohanea/whisper.net Whisper.net. Speech to text made simple using Whisper Models 模型下载地址&#xff1a;https://huggingface.co/sandrohanea/whisper.net/tree…

【wireshark】基础学习

TOC 查询tcp tcp 查询tcp握手请求的代码 tcp.flags.ack 0 确定tcp握手成功的代码 tcp.flags.ack 1 确定tcp连接请求的代码 tcp.flags.ack 0 and tcp.flags.syn 1 3次握手后确定发送成功的查询 tcp.flags.fin 1 查询某IP对外发送的数据 ip.src_host 192.168.73.134 查询某…

飞瓜数据B站丨B站UP主11月第3周榜单排行榜榜单(B站平台)发布!

飞瓜轻数发布2023年11月13日-11月19日飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数、带货数据等维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营…

竞赛选题 酒店评价的情感倾向分析

前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 酒店评价的情感倾向分析 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/post…

Docker Swarm总结(2/3)

目录 8、service 操作 8.1 task 伸缩 8.2 task 容错 8.3 服务删除 8.4 滚动更新 8.5 更新回滚 9、service 全局部署模式 9.1 环境变更 9.2 创建 service 9.3 task 伸缩 10、overlay 网络 10.1 测试环境 1搭建 10.2 overlay 网络概述 10.3 docker_gwbridg 网络基础…

cocos2dx ​​Animate3D (一)

3D相关的动画都是继承Grid3DAction 本质上是用GirdBase进行创建动画的小块。 Shaky3D 晃动特效 // 持续时间(时间过后不会回到原来的样子) // 整个屏幕被分成几行几列 // 晃动的范围 // z轴是否晃动 static Shaky3D* create(float initWithDuration, const Size& …

线程的几种状态

&#xff08;线程的几种状态&#xff09; 1.线程状态&#xff08;生命周期&#xff09; 1.1.源码中的状态 关于Java线程的状态&#xff0c;网上说法很多&#xff0c;有五种、六种甚至七种&#xff0c;本文采用Java官方的线程状态分类。 实际上&#xff0c;官方一共给出了六种…

[点云分割] 基于最小切割的分割

效果&#xff1a; 代码&#xff1a; #include <iostream> #include <vector>#include <pcl/point_types.h> #include <pcl/io/pcd_io.h> #include <pcl/visualization/cloud_viewer.h> #include <pcl/filters/filter_indices.h> #include…

基本的弹层,点击弹出

<div class"role"><el-button type"primary" size"mini" click"showDialog true">添加角色</el-button></div><!--控制弹框的显示隐藏visible .sync可以点击X关闭弹框 --> <el-dialog width"…

在字节三年,我跳槽了...

前言 早上&#xff08;8:00-12:00&#xff09;8:00起床&#xff0c;到公司吃个早饭&#xff0c;挤电梯。看事故平台摸摸鱼&#xff0c;根据一天的会议情况安排时间&#xff0c;大概11:50跟同组同学去吃饭。 中午&#xff08;吃完饭后-14:00&#xff09;看情况&#xff0c;如果…