【Java】线程数据共享和安全 -ThreadLocal

news2025/1/12 8:53:35

 🎄欢迎来到@边境矢梦°的csdn博文🎄

 🎄本文主要梳理线程数据共享和安全 -ThreadLocal🎄


🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生🌈
🎆喜欢的朋友可以关注一下🫰🫰🫰,下次更新不迷路🎆

Ps: 月亮越亮说明知识点越重要 (重要性或者难度越大)🌑🌒🌓🌔🌕    

在这里插入图片描述

目录

🎉 Java的有利武器:ThreadLocal 

🚀 第一章 - 什么是ThreadLocal?

📝第二章 - ThreadLocal原理

  📌源码分析

📜 第三章 - 如何使用ThreadLocal

🌟 第四章 - ThreadLocal的应用场景

🔮总结


🎉 Java的有利武器:ThreadLocal 🎉

📣今天我要为大家推荐一个Java中非常实用且神奇的工具——ThreadLocal。它可以让我们在多线程环境下,轻松地实现线程私有的数据存储。它可以帮助我们在多线程环境下轻松解决变量共享和线程安全的问题。🔍


🚀 第一章 - 什么是ThreadLocal?

1. ThreadLocal 的作用,可以实现在同一个线程数据共享 , 从而解决多线程数据安全问题 .
2. ThreadLocal 可以给当前线程关联一个数据 ( 普通变量、对象、数组 )set 方法 [ 源码 !]
3. ThreadLocal 可以像 Map 一样存取数据, key 为当前线程 , get 方法
4. 每一个 ThreadLocal 对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数 据,就需要使用多个 ThreadLocal 对象实例
5. 每个 ThreadLocal 对象实例定义的时候,一般为 static 类型
6. ThreadLocal 中保存数据,在线程销毁后,会自动释放

📷 图一:ThreadLocal示意图


📝第二章 - ThreadLocal原理

⚠️一个ThreadLocal可以同时给多个线程分别关联各自的数据。每个线程在访问ThreadLocal时,都会获取到自己独立的数据副本,互不干扰。这就是ThreadLocal的独立性和隔离性所表现出来的特点。

📷 图二:ThreadLocal同时给多个线程分别关联各自的数据示意图

⚠️ThreadLocal是用于实现线程局部变量的机制,它为每个线程提供了一个独立的副本,保证了线程的隔离性。因此,一个ThreadLocal变量最多只能关联一个线程的数据。

📷 图三:ThreadLocal一个线程一个数据示意图


  📌源码分析

实际上,ThreadLocal本身并没有存储任何对象,所有的东西都存储在Thread对象本身里!

首先,在Thread对象(注意,不是ThreadLocal!)里有个成员变量:

ThreadLocal.ThreadLocalMap threadLocals = null;

这个成员变量的类型是ThreadLocal.ThreadLocalMap,这是ThreadLocal的一个内部类,可以理解为一个简单的HashMap(关于这个内部类的实现本篇就不详细展开了,有兴趣的同学可以看源码),就是保存键值对的一个容器。

其实,所有set到ThreadLocal上的对象,实际都保存在当前Thread中的threadLocals成员变量里。

为了说的清楚,下面我用伪代码的形式,写一下ThreadLocal的Set()方法的核心逻辑:

public void set(T value) {
        Thread t = Thread.currentThread(); //先获得当前线程
        if (t.threadLocals != null){ //判断当前线程上的threadLocals成员是否为空
            t.threadLocals.set(this, value); //如果threadLocals不等于空,则set value,ThreadLocal对象作为key值。
        }else{
            t.threadLocals = new ThreadLocalMap(); //如果threadLocals为空,则创建之然后再set value。
            t.threadLocals.set(this, value); 
        }
    }

通过以上代码大家应该都能看清楚,真正的value值是保存在Thread对象上的,同时,key值是ThreadLocal对象。

再看看get方法,依然是只有核心逻辑的伪代码:

public T get() {
        Thread t = Thread.currentThread(); //先获得当前线程
        if (t.threadLocals != null) { //判断当前线程上的threadLocals成员是否为空
            return (T)t.threadLocals.get(this); //不为空,则以ThreadLocal对象作为key值得到value,然后返回。
        }
        return setInitialValue(); //返回初始值
    }

理清了get和set方法,相信整个ThreadLocal的实现方式,大家应该都比较清楚了。

 来自简书. 作者:我爱纽约先生的一段话(链接在文末)

作者之所以这样设计,我相信是经过了充分考虑的。我能想到的一点,就是考虑了对象的生命周期。

ThreadLocal要为每个线程维护value值,所以它的生命周期一般是长于线程对象生命周期的。一个长生命周期的对象维护一个短生命周期对象的引用,就有可能内存泄露,除非开发者要手动remove掉。

而把value存放在Thread上,一切就可以完美的解决。因为这个value对象是专门为Thread对象而存在的,所以value对象和Thread对象的生命周期应该完全一致。把value对象存放在Thread对象里,则不会有内存泄露的风险。虽然,Thread会维护一个ThreadLocal的引用(实际上是个弱引用),但是如前所说,一个短生命周期的对象维护长生命周期对象的引用,一般没什么问题。


📜 第三章 - 如何使用ThreadLocal

使用ThreadLocal非常简单。我们只需创建一个ThreadLocal对象,并重写initialValue()方法,定义每个线程初始的变量值。然后,通过调用get()方法可以获取当前线程的副本,而set()方法用于设置线程局部变量的值。

public class ThreadLocalExample {
    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            // 设置每个线程的初始值为0
            return 0;
        }
    };
    
    public static void main(String[] args) {
        // 创建3个线程执行任务
        Thread t1 = new Thread(new MyRunnable());
        Thread t2 = new Thread(new MyRunnable());
        Thread t3 = new Thread(new MyRunnable());

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static class MyRunnable implements Runnable {
        @Override
        public void run() {
            int value = threadLocal.get();
            System.out.println("线程:" + Thread.currentThread().getName() + ",初始值:" + value);
            
            // 修改初始值
            value += 5;
            threadLocal.set(value);
            
            // 再次获取修改后的值
            int updatedValue = threadLocal.get();
            System.out.println("线程:" + Thread.currentThread().getName() + ",修改后的值:" + updatedValue);
        }
    }
}

 在上述代码中,我们创建了一个名为threadLocal的ThreadLocal对象,并重写了它的initialValue()方法,用于定义每个线程初始的变量值(这里设置为0)。

然后,我们创建了3个线程,并在每个线程的run()方法中操作ThreadLocal对象。首先,通过get()方法获取当前线程的副本,并输出初始值。然后,我们使用set()方法修改初始值,并再次使用get()方法获取修改后的值。

运行这段代码,你会看到每个线程都有自己独立的初始值和修改后的值。这表明每个线程通过ThreadLocal对象进行了数据隔离,互不干扰。

你可以根据自己的需求来定义ThreadLocal对象的值和操作逻辑。


🌟 第四章 - ThreadLocal的应用场景

ThreadLocal的应用非常广泛,下面为大家介绍几个特别实用的场景:

1️⃣ 数据库连接管理:在多线程环境下,使用ThreadLocal可以方便地管理数据库连接,每个线程都能获取到自己独立的连接副本,避免了线程安全和资源竞争问题。

2️⃣ Web应用用户信息存储:对于一个Web应用程序,可以使用ThreadLocal将当前用户的登录信息存储在线程局部变量中,这样在后续的请求处理过程中就无需传递这些信息了。

3️⃣ 日志跟踪:ThreadLocal还可以用于日志跟踪,每个线程都有自己的日志副本,方便调试和定位问题。


🔮总结

💡Thread里面存储着各自的ThreadLocalMap, 并且Thread的每一个ThreadLocal会根据Thread的生命周期进行销毁, 每个ThreadLocalMap都属于某一个Thread, 而ThreadLocal只是ThreadLocalMap里面的一个而已, ThreadLocal对象实例就是Map中的键, 根据它就可以得到value, 一个ThreadLocal只能为一个Thread服务一个数据, 但是同时可以为其他Thread服务.

💡为什么是一个ThreadLocal给Thread关联一个数据呢?

因为set()方法中, t.threadLocals.set(this, value); //如果threadLocals不等于空,则set value,ThreadLocal对象作为key值。

不管你怎么弄都是一个ThreadLocal对应着一个相应的Thread的

📷 图四:ThreadLocal原理图

🎉 精彩的ThreadLocal世界就在眼前!无论你是面对高并发的系统,还是只是想学习Java多线程编程,ThreadLocal都能帮助你轻松解决问题。希望这篇推荐能给你带来灵感与启发。如果你有其他关于ThreadLocal的经验和想法,也欢迎在评论区与我分享。下次再见!👋

🫰🫰🫰以下是对我帮助很多的文章:

ThreadLocal到底是个什么东西? - 简书 (jianshu.com)

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

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

相关文章

CAN-FD总线通信应用理解

传统的车载CAN总线最高支持500 kbit/s的传输速率,每帧只能承载8 bytes的数据,由于传输速率和数据长度的限制,在自动驾驶和智能网联对网络通信的高要求背景下,使用传统 CAN 通信势必会导致总线负载率过高从而导致网络拥堵,传统CAN总线通信的瓶颈逐渐凸显。 2011年,为满足带…

并发相关面试题

巩固基础&#xff0c;砥砺前行 。 只有不断重复&#xff0c;才能做到超越自己。 能坚持把简单的事情做到极致&#xff0c;也是不容易的。 如何理解volatile关键字 在并发领域中&#xff0c;存在三大特性&#xff1a;原子性、有序性、可见性。volatile关键字用来修饰对象的属性…

HCIP学习--BGP2

目录 前置内容 BGP宣告问题 BGP自动汇总问题 BGP 的认证 BGP的聚合(汇总) 标准的BGP聚合配置 非标准的BGP聚合 路由传递干涉策略 抑制列表 Route-map 分发列表 前缀列表 BGP在MA网络中下一跳问题-ICMP重定向 查看与某个邻居收发的路由 配置 有条件打破IBGP水平…

MyBatis框架:创建Mapper接口和映射文件,实现基本增删改查

目录 1、Mapper接口和映射文件关系 2、Mapper接口和映射文件的命名规则 2.1 Mapper接口的命名规则 2.2 映射文件的命名规则 3、Mapper接口和映射文件的创建及增删改查的实现 3.1 Mapper接口和映射文件的创建 3.2 增删改查的实现 3.2.1表结构 3.2.2 创建表User对应的实体…

excel 之 VBA

1、excel和VBA 高效办公&#xff0c;把重复性的工作写成VBA代码&#xff08;VB代码的衍生物&#xff0c;语法和VBA相同&#xff09;。 首先打开开发工具模式&#xff0c;如果没有选显卡&#xff0c;需要手动打开 打开程序编辑界面 快捷键 altF11一般操作 程序调试&#xf…

详解JAVA远程debug

目录 1.什么是远程debug&#xff1f; 2.远程debug普通JAVA程序 环境 测试程序 程序启动指令 编译器配置 3.远程debug JAVA Web程序 4.远程debug spring boot程序 1.什么是远程debug&#xff1f; 远程debug&#xff0c;也就是可以在本地debug远端部署的程序&#xff0c…

深入浅出:MyBatis的使用方法及最佳实践

这里写目录标题 添加MyBatis框架⽀持配置连接字符串和MyBatis配置连接字符串配置 MyBatis 中的 XML 路径 添加业务代码创建数据库和表添加用户实体类添加 mapper 接⼝添加 UserMapper.xml添加 Service层添加 Controller层 增删改操作增加操作删除操作修改操作 添加MyBatis框架⽀…

vue3+ts+vite全局配置Element-Plus主题色

概述 我找了很多博客&#xff0c;想全局配置Elmenet-Plus组件主题色&#xff0c;但都没有效果。所以有了这篇博客&#xff0c;希望能对你有所帮助&#xff01;&#xff01;&#xff01; 文章目录 概述一、先看效果二、创建全局颜色文件2.1 /src/styles 下新建 element-plus.sc…

王道机组难题分析

第四章 指令系统 大端方式&#xff1a;就是高地址存放高位&#xff0c; LSB的意思是&#xff1a;全称为Least Significant Bit&#xff0c;在二进制数中意为最低有效位 MSB的意思是&#xff1a;全称为Most Significant Bit&#xff0c;在二进制数中属于最高有效位 操作数可以理…

设备工单管理系统如何实现工单流程自动化?

设备工单管理系统属于工单系统的一种&#xff0c;基于其丰富的功能&#xff0c;它可以同时处理不同的多组流程&#xff0c;旨在有效处理发起人提交的事情&#xff0c;指派相应人员完成服务请求和记录全流程。该系统主要面向后勤管理、设备维护、物业管理、酒店民宿等服务行业设…

微服务07-分布式缓存

前提: 单机的Redis存在四大问题: 解决办法:基于Redis集群解决单机Redis存在的问题 1、Redis持久化 Redis有两种持久化方案: RDB持久化AOF持久化1.1 RDB持久化 RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所…

redis的基础命令01

1、操作库的指令 1、清除当前库---flushdb 2、清除所有库---flushAll 2、操作key的指令 最常用的指令get、set 1&#xff09;set key value 2&#xff09;get key 基础指令 1、del 删除单个&#xff1a;del key 、批量删除&#xff1a;del key1 key2 key3 2、exists 判断key是否…

jpa查询返回自定义对象、返回指定VO、POJO

jpa查询返回自定义对象、返回指定VO、POJO jpa查询返回自定义对象、返回指定VO、POJO&#xff0c;JPA查询前会做大量处理&#xff0c;还有线程通知的操作。若并发大&#xff0c;处理性能直线下降。但是jpa就因为做了大量处理&#xff0c;对多数据库兼容极好&#xff0c;操作方…

QT之UDP通信

QT之UDP通信 UDP不分客户端口服务器,只需要使用一个类QUdpSocket QT += core gui networkgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = udp TEMPLATE = app# The following define makes your compiler emit warnings if you use # any feature of Qt …

通义大模型:打造更智能、更灵活的自然语言处理技术

大家好&#xff0c;今天我想向大家介绍一款备受瞩目的自然语言处理技术——通义大模型。作为一种基于深度学习的人工智能技术&#xff0c;通义大模型能够模拟人类的思维方式&#xff0c;实现更智能、更灵活的自然语言处理&#xff0c;为我们的生活和工作带来了极大的便利。 在…

Java解决四大查找(一)

Java解决四大查找 一.线性查找1.1 题目1.2 思路分析1.3 代码演示 二.二分查找(双指针法)2.1 题目2.2 思路分析(图解加文字)2.3 代码演示 一.线性查找 1.1 题目 在数组{1&#xff0c;8&#xff0c;1024&#xff0c;521&#xff0c;1889}中查找数字8&#xff0c;如果有&#xff…

数学建模(二)线性规划

课程推荐&#xff1a;6 线性规划模型基本原理与编程实现_哔哩哔哩_bilibili 在人们的生产实践中&#xff0c;经常会遇到如何利用现有资源来安排生产&#xff0c;以取得最大经济效益的问题。此类问题构成了运筹学的一个重要分支&#xff1a;数学规划。而线性规划(Linear Program…

基于 FPGA 的电机控制

FPGA 非常适合精密电机控制&#xff0c;在这个项目中&#xff0c;我们将创建一个简单的电机控制程序&#xff0c;在此基础上可以构建更复杂的应用。 需要的硬件 Digilent Pmod HB3 介绍 我们可以用一个简单的 8 位微控制器来控制电机&#xff0c;输出一个简单的脉宽调制波形。然…

201、仿真-基于51单片机PT100测温设计铂电阻温度计设计Proteus仿真(程序+Proteus仿真+原理图+流程图+元器件清单+配套资料等)

毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、设计功能 二、Proteus仿真图 三、原理图 四、程序源码 资料包括&#xff1a; 方案选择 单片机的选择 方案一&#xff1a;STM32系列单片机控制&#xff0c;该型号单片机为LQFP44封装&#xff0c;内部资源足够用于本次设…

Linux学习之sed保持空间

echo 1#sed#s >> holdSpaceTest.txt echo 2#deep#d >> holdSpaceTest.txt echo 3#good#g >> holdSpaceTest.txt echo 4#hood#h >> holdSpaceTest.txt把下边的内容写入到holdSpaceTest.txt中&#xff1a; 1#sed#s 2#deep#d 3#good#g 4#hood#htac holdS…