【Java】ThreadLocal原理与使用场景

news2025/7/8 11:01:13

ThreadLocal原理:

字段:

//ThreadLocal对象的哈希码
private final int threadLocalHashCode = nextHashCode();


//生成ThreadLocal对象的哈希码时,需要用到该对象,从0开始
private static AtomicInteger nextHashCode =
        new AtomicInteger();


//哈希码的增长值
private static final int HASH_INCREMENT = 0x61c88647;


        nextHashCode()方法:


private static int nextHashCode() {
        //通过AtomicInteger对象生成ThreadLocal对象的哈希码并返回
        //步长为:HASH_INCREMENT
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

方法实现:

        (1)get()方法:

public T get() {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        //判断ThreadLocalMap对象是否为空
        //map不为null说明有键值对
        if (map != null) {
            //在map中,ThreadLocal作为key
            ThreadLocalMap.Entry e = map.getEntry(this);
            //如果key不为空
            if (e != null) {
                @SuppressWarnings("unchecked")
                //通过key获取值并返回
                T result = (T)e.value;
                return result;
            }
        }
        //如果map为null,则创建map
        //当前ThreadLocal对象作为key,null作为value存入map中
        //返回null
        return setInitialValue();
    }

        (2)set()方法:

public void set(T value) {
        //获取当前线程的ThreadLocalMap对象
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        
        //如果map不为null
        if (map != null)
            //将当前ThreadLocal作为key
            //参数value作为值,存入ThreadLocalMap中
            map.set(this, value);
        else
            //map为null,则创建map
            //将当前ThreadLocal对象作为firstKey,参数value作为firstValue
            createMap(t, value);
    }

通过set方法,我们可以知道:

        1、线程调用ThreadLocal对象的set方法时,会获取当前线程的ThreadLocalMap对象。

        2、随后会将当前ThreadLocal对象作为key,参数value作为值存储到线程内部的ThreadLocalMap对象中。

        3、这意味着即使ThreadLocal是多个线程共享的变量也不会存在线程安全问题,因为每个线程都只在操作自己的ThreadLocalMap,ThreadLocal只是作为key保存在map中。

        (3)remove()方法:

public void remove() {
         //获取当前线程的ThreadLocalMap对象
         ThreadLocalMap m = getMap(Thread.currentThread());
         //如果map不为null
         if (m != null)
             //将当前ThreadLocal对象从ThreadLocalMap中移除
             m.remove(this);
     }

ThreadLocalMap、ThreadLocal、Thread三者之间的联系:

测试:

        (1)User类:

package test;

public class User {

    private Integer id;

    private String name;


    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }


    public User(){

    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

        (2)UserHolder类:

package test;

public class UserHolder {


    private static final ThreadLocal<User> threadLocal = new ThreadLocal<>();

    public static void set(User user){
        threadLocal.set(user);
    }


    public static User get(){
        User user = threadLocal.get();
        return user;
    }
}

       ThreadLocal作为静态字段存在于UserHolder类中,UserHolder的set方法、get方法实际上都是由ThreadLocal实现,这里使用到了组合的思想。

        (3)ThreadLocalTest类:

package test;

public class ThreadLocalTest {


    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            System.out.println("Thread-1线程运行,存储User对象到TheadLocal中...");
            createAndHold(1,"张三");
            User user = UserHolder.get();
            System.out.println(user);
        }, "Thread-1").start();
        
        System.out.println("main线程阻塞...");
        Thread.sleep(3000);
        System.out.println("main线程运行,获取UserHolder存储的User对象...");
        User user = UserHolder.get();
        System.out.println("user对象:" + user);
    }

    private static void createAndHold(Integer id,String name){
        User user = new User(id, name);
        UserHolder.set(user);
    }
}

代码解读:

        1、main线程一开始会阻塞3s,Thread-1线程运行,将User对象存入ThreadLocal并打印。

        2、main线程解除阻塞后,会去获取ThreadLocal中存储的User对象;照理来说,ThreadLocal对象是静态成员,对于两个线程来说是共享变量,此时main线程应该会获取到Thread-1存入的User对象。

        3、但我们知道,ThreadLocal真正操作的是每个线程内部的ThreadLocalMap,ThreadLocal对象只作为key存储到每个线程自己的ThreadLocalMap中,我们可以通过key找到value,实现线程间的隔离。

测试结果:

ThreadLocal使用场景:

        (1)多线程隔离 多个线程使用ThreadLocal访问共享变量,每个线程都会得到共享变量的一个副本,后续多个线程自己所属副本的所有操作不会冲突,没有线程安全问题。

        (2)线程内共享ThreadLocal 可以用于在整个线程生命周期内共享这些上下文信息,而不需要显式地传递参数。

        (3)存储用户信息在Web应用中,可能会在用户登录后将用户身份信息存储在 ThreadLocal 中,以便在整个请求处理过程中方便地访问用户信息,而不必在每个方法中都传递用户信息参数。

       

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

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

相关文章

文件分片上传(模拟网盘效果)

文件分片上传&#xff08;模拟网盘效果&#xff09; 文章说明简单模拟拖拽文件夹和选择文件的进度条效果效果展示结合后端实现文件上传效果展示加上分片的效果效果展示加上MD5的校验&#xff0c;实现秒传和分片的效果后续开发说明源码下载 文章说明 文章主要为了学习文件上传&a…

图像拼接——基于homography的特征匹配算法

目录 1. 任务要求2. 数据集3. 基于homography的特征匹配算法4. 拼接流程展示4.1 图片实例4.2 特征点位图4.3 特征点匹配结果4.4 相机校准结果4.5 拼接结果 5. 部分图像拼接结果展示 1. 任务要求 输入&#xff1a;同一个场景的两张待拼接图像&#xff08;有部分场景重合&#x…

统信系统常见问题解决方法

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 背景说明 本文所说的问题&#xff0c;是基于浪潮统信UOS的环境存在的问题。 一、WPS新建文档默认保存格式不对 解决办法&#xff1a; 1.编辑/opt/apps/cn.wps.wps-office-pro/files/kingsoft/wps-office/…

计算机网络【Google的TCP BBR拥塞控制算法深度解析】

Google的TCP BBR拥塞控制算法深度解析 宏观背景下的BBR 慢启动、拥塞避免、快速重传、快速恢复&#xff1a; 说实话&#xff0c;这些机制完美适应了1980年代的网络特征&#xff0c;低带宽&#xff0c;浅缓存队列&#xff0c;美好持续到了2000年代。 随后互联网大爆发&#x…

【中南林业科技大学】计算机组成原理复习包括题目讲解(超详细)

来都来了点个赞收藏关注一下再走呗&#x1f339;&#x1f339;&#x1f339;&#x1f339; 第1章&#xff1a;绪论 1.冯诺依曼机特点&#xff0c;与现代计算机的区别 冯诺依曼计算机的基本思想是&#xff1a;程序和数据以二进制形式表示&#xff0c;存储程序控制。在计算机中&…

Android14新特性 开启前台service服务

1. Android14新特性 1.1. 场景 在Android14&#xff08;targetSDK34&#xff09;系统手机开启前台service服务崩溃 ATAL EXCEPTION: mainProcess: com.inspur.lbrd, PID: 15634java.lang.RuntimeException: Unable to create service com.inspur.lbrd.service.KeepAliveServi…

[GDOUCTF 2023]泄露的伪装

[GDOUCTF 2023]泄露的伪装 wp 进入页面&#xff0c;会发现什么也没有&#xff1a; 目录扫描&#xff1a; dirsearch -u “http://node4.anna.nssctf.cn:28588/” 扫出了两个文件&#xff0c;都去访问一下&#xff0c;test.txt 是源码的副本&#xff0c;由于是文本文件&…

STL——集合算法

算法简介&#xff1a; set_intersection // 求两个容器的交集set_union // 求两个容器的并集set_difference // 求两个容器的差集 1.set_intersection 函数原型&#xff1a; set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);…

NFS的基本使用

#江南的江 #每日鸡汤&#xff1a;岁月匆匆&#xff0c;时光荏苒&#xff0c;感悟人生路漫漫&#xff0c;不忘初心方得始终。 #初心和目标&#xff1a;和从前的自己博弈。 NFS(存储共享服务) 本文要点摘要&#xff1a; 下面将讨论什么是NFS&#xff0c;如何配置NFS&#xff0c;…

AI产品经理-借力

AI产品经理-借力&#xff1a;学会善用供应商改造自有产品 1.整个项目的工作方法 2.项目启动-行业调研 3.项目启动-供应商选型

【数据结构——二叉树】二叉树及其应用2023(头歌习题)【合集】

目录 第1关&#xff1a;括号表示法创建二叉树任务描述相关知识编程要求测试说明完整代码 第2关&#xff1a;先序序列创建二叉树任务描述相关知识二叉树的前序遍历如何创建一颗二叉树伪代码如下&#xff1a; 二叉树的中序遍历 编程要求测试说明完整代码 第3关&#xff1a;计算二…

蓝桥杯C/C++程序设计——单词分析

题目描述 小蓝正在学习一门神奇的语言&#xff0c;这门语言中的单词都是由小写英文字母组 成&#xff0c;有些单词很长&#xff0c;远远超过正常英文单词的长度。小蓝学了很长时间也记不住一些单词&#xff0c;他准备不再完全记忆这些单词&#xff0c;而是根据单词中哪个字母出…

Python魔法方法之__getattr__和getattribute

在Python中有这两个魔法方法容易让人混淆&#xff1a;__getattr__和getattribute。通常我们会定义__getattr__而从来不会定义getattribute&#xff0c;下面我们来看看这两个的区别。 __getattr__魔法方法 class MyClass:def __init__(self, x):self.x xdef __getattr__(self, …

HTML标签基础入门

HTML 基本语法概述标签关系HTML基础结构HTML常用标签标题标签示例 段落和换行标签示例 文本格式化标签示例 div和span标签示例 图像标签和路径示例 超链接标签示例 注释 ctrl/特殊字符示例 表格标签 表头单元格标签表格属性示例 合并单元格示例 列表标签无序列表有序列表自定义…

SpringBoot集成支付宝,看这一篇就够了。

前 言 在开始集成支付宝支付之前&#xff0c;我们需要准备一个支付宝商家账户&#xff0c;如果是个人开发者&#xff0c;可以通过注册公司或者让有公司资质的单位进行授权&#xff0c;后续在集成相关API的时候需要提供这些信息。 下面我以电脑网页端在线支付为例&#xff0c;介…

Linux系统使用yum安装MySQL

部署MySQL数据库有多种部署方式&#xff0c;常用的部署方式就有三种&#xff1a;yum安装、rpm安装以及编译安装。每一种安装方式都有自己的优势&#xff0c;那么企业当中通常情况下采用的是rpm和二进制安装的方式。 MySQL官网下载地址 Mysql 5.7的主要特性 更好的性能&#xf…

费曼学习法应用:谈自私和教育的引导

今天这个还是来源于我和九迁的对话&#xff0c;起因是中午吃饭的时候&#xff0c;九迁在学校与班主任老师和数学老师对话中带来的思考。 先听音频&#xff1a; 对话内容&#xff08;以下内容可以边听边看&#xff0c;属于语音转换过来的文字&#xff0c;最后有个总结&#xff0…

中文字符占用字节即相关原理(实现中文(中英混合)字符串的反转)

如有不对欢迎指正。 目录 一.ASCLL字符和中文字符 1.使用无符号数表示的原因(对于中文字符)&#xff1a; 2.但是并不是所有情况都是用无符号数(以下目前只是猜测,如有问题欢迎指正) &#xff1a; 1. 什么时候使用无符号数表示: 2. 不需要使用的情况&#xff1a; …

46、激活函数 - Relu 激活

本节介绍一个在神经网络中非常常见的激活函数 - Relu 激活函数。 什么是ReLU激活函数 ReLU 英文名为 Rectified Linear Unit,又称修正线性单元,是一种简单但很有效的激活函数,它的定义如下: 即当输入 x 大于零时,输出等于他自己;当输入小于等于零时,输出为零,下面是re…

2023年成都市中等职业学校学生技能大赛“网络搭建及应用”赛项竞赛样卷

2023年成都市中等职业学校学生技能大赛 “网络搭建及应用”赛项竞赛样卷 &#xff08;总分1000分&#xff09; 目录 2023年成都市中等职业学校学生技能大赛 “网络搭建及应用”赛项竞赛样卷 网络建设与调试项目&#xff08;500分&#xff09; 服务器搭建与运维项目&#xff08;…