ThreadLocal机制解读和源码分析

news2024/10/6 4:05:05

目录

线程数据共享和安全 -ThreadLocal

什么是 ThreadLocal

代码演示

创建Dog.java

创建Pig.java

T2DAO.java

T2DAO

T1解读set

T1Service  解读 get

ThreadLocalTest这个是换一种法

ThreadLocal 原理分析图

1. ThreadLocal 原理分析图(重点 set 和 get)



线程数据共享和安全 -ThreadLocal

什么是 ThreadLocal

1. ThreadLocal 的作用,可以实现在同一个线程数据共享, 从而解决多线程数据安全问题.

2. ThreadLocal 可以给当前线程关联一个数据(普通变量、对象、数组)set 方法 [源码!]

3. ThreadLocal 可以像 Map 一样存取数据,key 为当前线程, get 方法

4. 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个 ThreadLocal 对象实例

5. 每个 ThreadLocal 对象实例定义的时候,一般为 static 类型

6. ThreadLocal 中保存数据,在线程销毁后,会自动释放

代码演示

创建Dog.java

public class Dog { }

创建Pig.java

public class Pig{ }

T2DAO.java

public class T2DAO {
    public void update() {
        String name = Thread.currentThread().getName();
        System.out.println("在 T2DAO 的 update() 线程是= " + name);
    }
}

T2DAO

public class T2DAO {
    public void update() {
        String name = Thread.currentThread().getName();
        System.out.println("在 T2DAO 的 update() 线程是= " +
                name + " threadlocal 数据是=" + ThreadLocalTest.threadLocal.get());
        System.out.println("在 T2DAO 的 update() 线程是= " +
                name + " threadlocal2 数据是=" + ThreadLocalTest.threadLocal2.get());
    }
}

T1解读set

1. 获取当前线程, 关联到当前线程!

2. 通过线程对象, 获取到ThreadLocalMap其中ThreadLocalMap是ThreadLocal的内部类

其中Entry又是ThreadLocalMap的内部类是存放真正的数据的地方 这同样也是为什么是一个线程只能存放一个对象 同样的线程放入会被覆盖

package com.threadlocal;

public class    T1 {

    //创建ThreadLocal对象, 做成public static.
    public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();
    public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();

    //Task 是线程类 -> 内部类 / 线程
    public static class Task implements Runnable {
        @Override
        public void run() {
            Dog dog = new Dog();
            Pig pig = new Pig();
            //给threadLocal1 对象放入set dog , 隔山打牛
            System.out.println("Task 放入了 dog= " + dog);
            /*
                解读
                public void set(T value) {
                    //1. 获取当前线程, 关联到当前线程!
                    Thread t = Thread.currentThread();
                    //2. 通过线程对象, 获取到ThreadLocalMap
                    //   ThreadLocalMap 类型 ThreadLocal.ThreadLocalMap
                    ThreadLocalMap map = getMap(t);
                    //3. 如果map不为null, 将数据(dog,pig..) 放入map -key:threadLocal value:存放的数据
                    //   从这个源码我们已然看出一个threadlocal只能关联一个数据,如果你set, 就会替换
                    //4. 如果map为null, 就创建一个和当前线程关联的ThreadLocalMap, 并且该数据放入
                    if (map != null)
                        map.set(this, value);
                    else
                        createMap(t, value);
                }

             */
            threadLocal1.set(dog);
            //threadLocal1.set(pig);//替换
            threadLocal2.set(pig);//这个数据就会threadLocal2关联,并且都被当前Thread管理
            System.out.println("Task 在run 方法中 线程=" + Thread.currentThread().getName());
            new T1Service().update();
        }
    }

    public static void main(String[] args) {
        new Thread(new Task()).start();//主线程启动一个新的线程,注意不是主线程
    }
}

T1Service  解读 get

1. 先得到当前的线程对象

2.通过线程获取到对应的ThrealLocalMap

3. 如果map不为空, 根据当前的 threadlocal对象,得到对应的Entry

4. 如果e 不为null就继续下面的

public class T1Service {

    public void update(){
        //取出threadLocal1对象关联的对象
        /**
         * 解读 get
         * public T get() {
         *          //1. 先得到当前的线程对象
         *         Thread t = Thread.currentThread();
         *         //2.通过线程获取到对应的ThrealLocalMap
         *         ThreadLocalMap map = getMap(t);
         *         if (map != null) {
         *              //3. 如果map不为空, 根据当前的 threadlocal对象,得到对应的Entry
         *             ThreadLocalMap.Entry e = map.getEntry(this);
         *             //4. 如果e 不为null
         *             if (e != null) {
         *                 @SuppressWarnings("unchecked")
         *                 //返回当前threadlocal关联的数据value
         *                 T result = (T)e.value;
         *                 return result;
         *             }
         *         }
         *         return setInitialValue();
         *     }
         *
         */
        Object o = T1.threadLocal1.get();
        //获取当前线程名
        String name = Thread.currentThread().getName();
        System.out.println("在T1Service的update() 线程name= " +
                name + " dog= " + o);
        //调用dao-update
        new T2DAO().update();
    }
}

ThreadLocalTest这个是换一种法

public class ThreadLocalTest {
    public static ThreadLocal<Object> threadLocal = new ThreadLocal<>();
    public static ThreadLocal<Object> threadLocal2 = new ThreadLocal<>();
    public static class Task implements Runnable {
        @Override
        public void run() {
            Dog dog = new Dog();
            Pig pig = new Pig();
                /*
                 * ======== set 源码分析 只要明白这个机制,后面的 set get 全部通透
           
                  public void set(T value) {
                 获取当前线程
                  Thread t = Thread.currentThread();
                  获取当前线程的 ThreadLocal.ThreadLocalMap 属性 threadLocals , 类型是 ThreadLocal 的静态内部类
                 //threadLocals 有 一 个 属 性 Entry[], 类 型
                 ThreadLocal.ThreadLocalMap.Entry
                  //k-> ThreadLocal 对象   v-> 值
                  ThreadLocalMap map = getMap(t);
                  if (map != null)
                  map.set(this, value);//存放这里的 this 就是 ThreadLocal, 可以
                 debug 源码,一目了然
                  else
                  createMap(t, value);//创建
                  }
                 
                 * ====== getMap 方法源码=====
                  ThreadLocalMap getMap(Thread t) { // 获取当前线程的
                 ThreadLocal.ThreadLocalMap
                  return t.threadLocals;
                  }
                 
                  说明:
                  1. ThreadLocalMap 对象是和当前 Thread 对象的绑定的属性
                  2. ThreadLocalMap 对象含有 Entry[] table; 这个 Entry(K,V)
                  3. 这个 key 就是 ThreadLocal 对象, V 就是你要在放入的对象,比如 dog
                  4. 当执行了 了 threadLocal.set(dog) 后,内存布局图为 wps[看图]
                 
                 */
            threadLocal.set(dog);
            threadLocal.set(pig);//会替换 dog
            //如果希望在同一个线程共享多个对象/数据,就在创建一个 ThreadLocal 对象
            //threadLocal2.set(pig);
            System.out.println("在 run 方法中 线程=" +
                    Thread.currentThread().getName() + " 放入 threadLocal 的数据=" + dog);
            new T1Service().update();
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 1; i++) {
            
            new Thread(new Task()).start();//启动一个新的线程,注意不是主线程
        }
        System.out.println("在 main 方法中 threadLocal 的数据=" + threadLocal.get());
    }
}

ThreadLocal 原理分析图

1. ThreadLocal 原理分析图(重点 set 和 get)

2. Debug 源码图

 

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

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

相关文章

Go Fuzzing:发现你未曾发现的漏洞

文章目录 Fuzzing(模糊测试)要求示例模拟crash 总结参考资料 Fuzzing(模糊测试) go fuzz文档 对于软件开发者而言&#xff0c;一项重要的任务就是确保程序的安全性。而其中一种风险就是软件中可能存在的漏洞。传统的测试方法往往需要耗费大量的时间和人力&#xff0c;而使用F…

【C++: 模块二 ---运算符、流程控制语句】

C&#xff1a; 模块二 ---运算符、流程控制语句 一、运算符&#xff1a;1.1算数运算符&#xff1a;1.2赋值运算符&#xff1a;1.3比较运算符&#xff1a;1.4逻辑运算符&#xff1a;1.5三目运算符&#xff1a; 二、程序流程结构2.1顺序结构&#xff1a;2.2选择结构&#xff1a;&…

ChatGPT免费第一版本

最近利用空余时间做了一个供大家免费体验的chatgpt国内可直接访问的版本 输入12345gpt.com可直接访问 贴上GPT给我回复的内容&#xff0c;&#x1f600; 当今社会&#xff0c;交流已经成为人们日常不可或缺的一部分。然而&#xff0c;随着技术的发展&#xff0c;人们对于交流工…

【Linux 裸机篇(七)】I.MX6U 中断系统

目录 一、中断向量表1. 中断向量偏移 二、中断系统简介1. 创建中断向量表 三、GIC 控制器简介1. 中断 ID 四、GIC 逻辑分块1. Distributor(分发器端)2. CPU Interface(CPU 接口端) 五、CP15 协处理器六、中断使能1. IRQ 和 FIQ 总中断使能2. ID0~ID1019 中断使能和禁止 七、中断…

【PXE高效的批量网络装机】

目录 一、PXE的概述1.1、PXE批量部署的优点1.2、搭建PXE满足的以下的前提条件1.3、搭建PXE远程安装 二、搭建PXE远程安装服务器1、安装并启动 TFTP 服务2、安装并启用 DHCP 服务3、准备 Linux 内核、初始化镜像文件4、准备PXE 引导程序5、安装FTP服务&#xff0c;准备CentOS 7 …

SpringMVC使用域对象共享数据

1、SpringMVC中的域对象 此处只有request、session、servletContext被使用&#xff0c;而page是jsp页面的域&#xff0c;不使用jsp。 request&#xff1a;一次请求的范围内session&#xff1a;一次会话的范围内servletContext&#xff1a;整个web的应用范围内 2、向request域…

MySQL高级第十七篇:数据库主从复制原理及保证数据一致性

MySQL高级第十七篇&#xff1a;数据库主从复制原理及保证数据一致性 一、概述1. 提升数据库的并发能力2. 主从复制的作用&#xff1f; 二、主从复制原理三、搭建一主一从环境四、如何解决数据一致性问题&#xff1f;1. 方案一、异步复制2. 方案二、半同步复制3. 方案三、组复制…

3.黑马Springboot原理篇自己修改笔记

原理篇 1.自动配置的工作流程 1.1 bean的加载方式 方式一&#xff1a;配置文件<bean/>标签 缺点&#xff1a;配置bean太繁琐 方式二&#xff1a;配置文件扫描注解定义bean⭐️ 获取bean方式 ①通过配置文件&#xff0c;扫描指定包&#xff0c;加载bean ②通过注解声…

C++ STL之string容器的模拟实现

目录 一、经典的string类问题 1.出现的问题 2.浅拷贝 3.深拷贝 二、string类的模拟实现 1.传统版的string类 2.现代版的string类&#xff08;采用移动语义&#xff09; 3.相关习题* 习题一 习题二 4.写时拷贝 5.完整版string类的模拟实现[注意重定义] MyString.h…

磁珠的工作原理

磁珠是一个耗能元器件&#xff0c;他能把频率相对较高的信号以热量的形式耗散掉&#xff0c;保留频率相对较低的信号。 主要有这种插件的磁珠&#xff0c;还有这种贴片的磁珠。 下面我们来看下磁珠具体工作原理。 磁珠的构造我们可以简单的看成一个导线穿过环形铁氧体的磁性材…

[渗透教程]-015-网络与系统渗透

文章目录 1.0基本概念2.0 网络与系统渗透基本原理2.1 渗透测试2.2 入侵和预防2.3 案例一:从信息收集到入侵提权2.3.1 从域名到ip2.3.2 从IP获取旁站2.3.3 收集系统与⽹络配置详细信息2.3.4 踩点2.3.5发现漏洞2.3.6漏洞利用2.3.7维持系统控制权2.3.8清理访问痕迹2.4 案例二:Goo…

TryHackMe-Jeff(boot2root | Hard?)

Jeff 你能破解杰夫的网络服务器吗&#xff1f; 如果你发现自己在暴力破解SSH&#xff0c;你就做错了。 端口扫描 循例nmap 进80&#xff0c;是一个空页面&#xff0c;查看源代码 将jeff.thm加入hosts 上gobuster /admin是空页面&#xff0c;/backups也没东西&#xff0c;/up…

Centos安全加固策略

目录 密码安全策略 设置密码的有效期和最小长度 设置用户密码的复杂度 登录安全策略 设置用户远程登录的安全策略 安全的远程管理方式 访问控制 限制root用户登录 修改ssh 22端口 设置登录超时时间 限制IP访问 安全审计 审核策略开启 日志属性设置 查看系统登录…

基础巩固、探寻Java装箱和拆箱的奥妙!

前言 今天在逛某知名论坛的时候&#xff0c;看到一篇"请不要使用包装类型&#xff0c;避免造成性能损失"的文章。一下子就吸引了我的注意。大意就是&#xff0c;能用基本数据类型就尽量用基本数据类型&#xff0c;因为包装类型自动拆箱、装箱会带来性能损失尤其是循环…

函数式编程#3纯函数的概念

纯函数的概念 文章目录 纯函数的概念纯函数的两种形式&#xff1a;调用目标本身,不会改变函数内部,不受函数外部影响 函数的副作用如何理解"相同的输入得到相同的输出"不是纯函数的映射关系是纯函数的映射关系 纯函数的两种形式&#xff1a; 调用目标本身,不会改变 …

gcc编译 与交叉编译(x86 to arm) (一)单个文件编译

1.1、gcc编译单个c程序&#xff08;hello.c) gcc hello.c -o hello (hello是生成的可执行程序的名字&#xff09;1.2、交叉编译hello.c 源平台&#xff1a; UOS_X86_64 目标平台&#xff1a;UOS_arm 方法&#xff1a;使用现成的交叉编译工具链 参考资料&#xff1a;交叉编译…

【UE】water插件的简单使用

UE Editor版本&#xff1a;4.26 目录 一、岛屿外观修改 二、波浪参数设置 三、水体海洋的颜色设置 四、 水体河流 五、创建可浮在水体上的actor 一、岛屿外观修改 1. 保证“Landmass”和“Water”插件已启用 启用后&#xff0c;搜索water可以看到如下组件 2. 激活地形编…

LeetCode:6390. 滑动子数组的美丽值

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;6390. 滑动子数组的美丽值 题目描述&#xff1a;给你一个长度为 n 的整…

vulnhub DC:4渗透笔记

靶场下载地址:https://vulnhub.com/entry/dc416-2016,168/ 信息收集 使用nmap确定靶场ip地址 扫描ip确定开放端口 开放22 80端口&#xff0c;访问一下网页端(这边断了一次靶机ip改为192.168.100.138) 漏洞利用 登录框尝试爆破 发现用户名密码admin happy 登录进入后发现这里…

【自然语言处理】【大模型】LaMDA:用于对话应用程序的语言模型

LaMDA&#xff1a;用于对话应用程序的语言模型 《LaMDA: Language Models for Dialog Applications》 论文地址&#xff1a;https://arxiv.org/abs/2201.08239 相关博客 【自然语言处理】【大模型】LaMDA&#xff1a;用于对话应用程序的语言模型 【自然语言处理】【大模型】Dee…