Java并发编程学习笔记:ThreadLocal

news/2024/5/20 9:27:38 标签: java, juc

Java并发编程学习笔记:ThreadLocal

  • 一、ThreadLocal
  • 二、ThreadLocalMap
  • 三、内存泄漏
  • 四、场景应用

一、ThreadLocal

ThreadLocal的主要作用是为每个线程提供一个独立的变量副本,这样在多线程环境下,每个线程可以拥有自己的变量值,而不会与其他线程共享同一份变量值,从而避免了因并发访问带来的数据同步问题。

基本用法:

  • 每个 ThreadLocal 实例都维护了一个与当前执行线程关联的变量副本。

  • 线程通过调用 ThreadLocal 的 set(T value) 方法来设置其对应的变量值,并且只能被当前线程读取或修改。

  • 要获取当前线程所绑定的变量值,使用 get() 方法,返回的是当前线程私有的那个变量副本。

  • 如果需要清除当前线程绑定的变量,可以调用 remove() 方法。

应用场景:

  • 在多线程环境中,每个线程可能需要维护自己的上下文信息,例如数据库连接、用户会话、日志记录器等。
  • 避免频繁传递相同的参数给同一个线程中的多个方法。
  • 在无需进行昂贵同步操作的情况下实现线程安全。

二、ThreadLocalMap

ThreadLocal 的底层原理主要围绕其如何为每个线程维护独立的变量副本。

ThreadLocal 并不直接存储变量值,而是通过一个内部类 ThreadLocalMap 间接实现对线程私有变量的管理。ThreadLocalMap 是一个定制化的哈希表,它的键是 ThreadLocal 实例本身(弱引用),值是需要在每个线程中保持独立状态的对象(强引用)。

  1. 当调用set(T value) 方法时,会根据当前执行线程找到与之关联的 ThreadLocalMap,然后将 value 存储到该 ThreadLocalMap 中,键就是当前 ThreadLocal 实例自身。

  2. 调用 get() 方法时,同样会从当前执行线程的 ThreadLocalMap 中查找对应的键(即 ThreadLocal 实例),并返回相应的值。如果之前没有设置过值,则返回初始值或 null。

  3. 每个 Thread 对象都有一个名为 threadLocals 或 inheritableThreadLocals 的成员变量,它们都是 ThreadLocalMap 类型的,用于存储不同 ThreadLocal 对象所绑定的线程局部变量。

三、内存泄漏

ThreadLocalMap 使用的是弱引用作为键,当 ThreadLocal 对象在其他地方没有强引用指向它时,垃圾回收器可能会回收这个 ThreadLocal 对象,而 ThreadLocalMap 中对应的条目因为键已经变为 null,所以不会被自动清理,从而导致 ThreadLocalMap 中存在无用但无法被回收的 Entry,形成内存泄漏。

为了避免这个问题,在 ThreadLocalMap 的 get()、set() 和 remove() 方法中都加入了检查和清理机制,以确保在访问时能够及时移除那些已经没有强引用的 ThreadLocal 对象关联的值。

ThreadLocal 底层原理的核心在于利用了 Java 弱引用机制以及为每个线程创建了一个自定义的 Map 结构来存放线程局部变量,从而实现了线程间数据的隔离。 同时,它也需要注意内存泄漏的风险,并通过内部逻辑进行了一定程度上的优化处理。

四、场景应用

一个简单的 ThreadLocal 使用案例,将使用它来为每个线程维护一个独立的计数器。

  1. 创建了一个 ThreadLocal 变量 threadLocalCounter,用于存储每个线程的独立计数器。

  2. 每个线程在开始执行时都会设置自己的计数器初始值,并在循环中不断更新这个计数器。

  3. 每个线程都有各自的 ThreadLocal 变量副本,所以线程之间的计数器是相互独立的,互不影响。

  4. 主线程尝试访问 threadLocalCounter 的值,主线程没有设置过该变量,输出的是默认值 null。

java">public class ThreadLocalSimpleExample {

    // 创建一个 ThreadLocal 变量用于存储每个线程的计数器
    public static final ThreadLocal<Integer> threadLocalCounter = new ThreadLocal<>();

    public static void main(String[] args) {
        // 创建两个线程
        Thread thread1 = new Thread(() -> {
            // 为当前线程设置初始计数值
            threadLocalCounter.set(0);

            // 在线程中执行任务并更新计数器
            for (int i = 0; i < 5; i++) {
                int currentCount = threadLocalCounter.get();
                threadLocalCounter.set(currentCount + 1);
                System.out.println("Thread 1: Counter value is " + threadLocalCounter.get());
            }
        });

        Thread thread2 = new Thread(() -> {
            // 为当前线程设置初始计数值
            threadLocalCounter.set(0);

            // 在线程中执行任务并更新计数器
            for (int i = 0; i < 3; i++) {
                int currentCount = threadLocalCounter.get();
                threadLocalCounter.set(currentCount + 1);
                System.out.println("Thread 2: Counter value is " + threadLocalCounter.get());
            }
        });

        // 启动两个线程
        thread1.start();
        thread2.start();

        // 等待所有线程结束
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 主线程尝试访问计数器(主线程没有设置过计数器,因此会返回默认值 null)
        System.out.println("Main Thread: Counter value is " + threadLocalCounter.get());
    }
}

http://www.niftyadmin.cn/n/5434364.html

相关文章

【工具】一键生成动态歌词字幕

那眼神如此熟悉 让人着迷无力抗拒 一次又一次相遇 在眼前却遥不可及 命运总爱淘气 将一切都藏匿 曾有你的回忆 无痕迹 若不是心心相吸 又怎么会一步一步靠近 &#x1f3b5; 董真《思如雪》 下载LRC歌词 https://www.musicenc.com/article/50287.htmlhttp…

NCV7356D1R2G接口集成芯片中文资料PDF数据手册参数引脚图规格书价格图片

产品概述&#xff1a; NCV7356 是一款用于单线数据链路的物理层器件&#xff0c;能够使用多种具碰撞分解的载波感测多重存取 (CSMA/CR) 协议运行&#xff0c;如博世控制器区域网络 (CAN) 2.0 版。此串行数据链路网络适用于不需要高速数据的应用&#xff0c;低速数据可在物理介…

Mysql 表设计范式

Mysql 表设计范式 文章目录 Mysql 表设计范式不满足第一范式&#xff08;1NF&#xff09;的表结构满足第一范式&#xff08;1NF&#xff09;的表结构满足第二范式&#xff08;2NF&#xff09;的表结构满足第三范式&#xff08;3NF&#xff09;的表结构总结 MySQL的三个范式&…

面试算法-38-最小覆盖子串

题目 给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串&#xff0c;则返回空字符串 “” 。 注意&#xff1a; 对于 t 中重复字符&#xff0c;我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。 如果…

[VCTF2024纳新赛]-PWN:ezhp_code解析

查看保护 查看ida 简单来说就是创建堆块和删除堆块而已&#xff0c;创建堆块的函数附带有写入函数。 但这里要注意一个程序里面的特殊的地方 在我们创建堆块时&#xff0c;程序会先创建一个0xa0大小堆块&#xff0c;并且这个地方还有个特殊的check_handle函数&#xff0c;如果…

FreeRTOS入门基础

RTOS是为了更好地在嵌入式系统上实现多任务处理和时间敏感任务而设计的系统。它能确保任务在指定或预期的时间内得到处理。FreeRTOS是一款免费开源的RTOS&#xff0c;它广泛用于需要小型、预测性强、灵活系统的嵌入式设备。 创建第一个任务 任务函数&#xff1a;任务是通过函数…

JavaScript BOM 的概念(浏览器对象模型)

浏览器对象模型&#xff08; Browser object model &#xff09;简称 BOM 。 Js 通过 BOM 和浏览器进行交互&#xff0c;可以获取屏幕尺寸&#xff0c;窗口大小&#xff0c;页面地址&#xff0c;历史记录等浏览器相关信息&#xff0c;也可以控制浏览器执行某些 行为&#…

如何订阅Midjourney

Midjourney介绍 Midjourney作为目前AI绘画领域效果卓越且备受青睐的工具&#xff0c;对于新用户而言&#xff0c;可能无法享受到免费试用的机会。因此&#xff0c;为了持续利用这款软件进行绘画创作&#xff0c;用户需要购买会员资格并开通订阅服务。那么&#xff0c;关于midj…