ThreadLocal 本地线程变量详解

2023-12-16

概述

ThreadLocal 意为本地线程变量,即该变量只属于当前线程,对其他线程隔离

我们知道,一个普通变量如果被多线程访问会存在存在线程安全问题,这时我们可以使用 Synchronize 来保证该变量某一时刻只能有一个线程访问,从而解决并发安全问题

但如果这个变量并不需要被共享,那么就可以使用 ThreadLocal 为每个线程提供一个完全独立的变量副本,每个线程只操作自身拥有的副本,彼此互不干扰

简而言之,Synchronized 用于线程间的数据共享,同步机制采用采用时间换空间的方式,而 ThreadLocal 则用于线程间的数据隔离,采用空间换时间的方式


ThreadLocal 使用

public class ThreadLocalTest {
  
  // 有个User对象需要在不同线程之间进行隔离访问,可以定义ThreadLocal如下
  static ThreadLocal<User> userThreadLocal = new ThreadLocal<>();

  // 设置线程本地变量的内容
  public void setUser(User user) {
    userThreadLocal.set(user);
  }

  public void getUser() {
    // 获取线程本地变量的内容
    User user = userThreadLocal.get();
    user.setUsername(user.getUsername + "-" + Thread.currentThread().getName());
    System.out.println(user.getUsername());
    // 移除线程本地变量
    userThreadLocal.remove();
  }
}

测试代码如下

public class DemoTest {
  public static void main(String[] args) {
    ThreadLocalTest threadLocalTest = new ThreadLocalTest();
    for(int i = 0; i < 100; i++) {
      Thread thread = new Thread(() -> {
        User user = new User();
        user.setUsername("小明");
        threadLocalTest.setUser(user);
        threadLocalTest.getUser();
      });
      thread.setName(String.valueOf(i));
      thread.start();
    }
  }
}

ThreadLocal 原理

首先,Java 中的线程是一个 Thread 类的实例对象,对象可以定义私有的成员变量,这也是 ThreadLocal 能实现线程本地变量的基础

在 Thread 类中定义了一个 Map 类型的成员变量,用来保存该线程的所有本地变量

ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap 的 Entry 的定义如下,key 为 ThreadLocal 对象,v 就是我们要在线程之间隔离的对象

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

ThreadLocal::set 方法的源码如下:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
}

使用 set 方法赋值时,首先会获取当前线程 thread,并获取 thread 线程的 ThreadLocalMap 属性。如果 map 属性不为空,则直接更新 value 值,key 就是自身的 ThreadLocal,如果 map 为空,则实例化 threadLocalMap,并将 value 值初始化

ThreadLocal::get 方法的源码如下:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

使用 get 方法获取值,也是首先获取当前线程,再获取线程 ThreadLocalMap,如果 map 不为空就用自身 ThreadLocal 为 key 从 map 获取对应的 value

ThreadLocal::remove 方法的源码如下:

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null) {
        m.remove(this);
    }
}

remove 方法也是获取当前线程并获取 ThreadLocalMap,再将自身 ThreadLocal 为 key 对应的 value 移除

综合以上,我们知道 ThreadLocalMap 是线程的一个属性值,用来保存该线程的本地变量。ThreadLocal 能操作当前线程的 ThreadLocalMap,具体做法是以自身为 key 在 map 中存取值。因为每个线程的 ThreadLocalMap 都是独立的,所以每次使用 ThreadLocal 存取值都仅限于当前的线程,不会影响其他线程


ThreadLocal 内存泄露问题

内存泄露问题:指程序中动态分配的堆内存由于某种原因没有被释放或者无法释放,造成系统内存的浪费,导致程序运行速度减慢或者系统奔溃等严重后果。内存泄露堆积将会导致内存溢出

观察使用 ThreadLocal 时的内存布局

当 ThreadLocal Ref 被回收了,由于在 Entry 使用的是强引用,在 Current Thread 还存在的情况下就存在着到达 Entry 的引用链,无法清除掉 ThreadLocal 的内容,同时 Entry 的 value 也同样会被保留,也就是说使用了强引用会出现内存泄露问题

为此,ThreadLocal 在 Entry 使用了弱引用

static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        // Entry继承WeakReference,将key传入父级构造方法,从而形成弱引用
        super(k);
        value = v;
    }
}

再观察使用软引用后的内存布局

当 ThreadLocal Ref 被回收,由于在 Entry 使用的是弱引用,因此在下次垃圾回收的时候就会将 ThreadLocal 对象清除

但由于 ThreadLocalMap 仍然存在 Current Thread Ref 这个强引用,Entry 中 value 的值仍然无法清除,还是存在内存泄露的问题,虽然当线程生命周期结束,Current Thread Ref 这个强引用也会随之消失,value 会在下一次垃圾回收被清除,但如果使用线程池,那么线程会被重新放回线程池等待复用,那么强引用就会一直存在。因此,我们在每次在使用完之后需要手动的 remove 掉 Entry 对象

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ThreadLocal 本地线程变量详解 的相关文章

随机推荐

  • 大揭秘!Python处理办公自动化的10大场景!

    知乎上有个热门问题 Python 未来会成为大众办公常用编程工具吗 在编程世界里 Python已经是名副其实的网红了 曾经一个学汉语言的研究生 问我怎么学Python 因为他们课程论文里需要用到文本分析 用Python来跑数据 我和他说 你
  • 计算机网络中的通信子网主要有哪些功能?

    计算机网络中的通信子网主要具有以下功能 负责全网的数据通信 通信子网通过使用各种通信协议和传输控制功能 能够确保数据从一台主机安全 准确地传输到另一台主机 这包括数据的封装 解封装 传输控制 差错控制等过程 完成各种网络数据的处理 转换和交
  • 计算机网络中的通信子网:架构、协议与技术简介

    在计算机网络中 通信子网是负责实现主机之间以及主机与终端之间数据传输的核心部分 它由一系列硬件设备和通信协议组成 为上层应用提供可靠 高效和透明的数据传输服务 本文将详细介绍通信子网的架构 协议与技术 一 通信子网的架构 星型拓扑 星型拓扑
  • Python爬虫是否合法?

    Python爬虫是否合法的问题颇具争议 主要涉及到使用爬虫的目的 操作方式以及是否侵犯了其他人的权益 本文将介绍Python爬虫的合法性问题 并提供一些相关的法律指导和最佳实践 1 什么是Python爬虫 Python爬虫是一种自动化程序
  • 学python如何办公自动化?学这些就够了

    我们天天都在忙 究竟在忙些什么 查找各种文件 在一个个文件夹里来回穿梭 在TXT XLS XLSX DOC DOCX PPT PDF文档之间来回切换 复制 粘贴 运指如飞 打开几十个网页 以便及时获取信息 将各种数据输入系统 以及把数据填写
  • 超实用!34 个 Python 自动化办公库清单!

    今天给大家分析34个常用的Python自动化办公库 本次内容涵盖了 Excel Word PPT ODF PDF 邮件 微信 文件处理等所有能在办公场景实现自动化的库 希望能够对大家有所帮助 Python Excel自动化库 1 xlwin
  • wireshark使用

    1 抓包界面介绍 2 过滤 1 ip过滤 or 端口过滤 ip src 192 168 1 104 显示源地址为192 168 1 104的数据包列表 ip dst 192 168 1 104 显示目标地址为192 168 1 104的数据
  • 如何自学成 Python 大神?这里有些建议

    人生苦短 我用 Python 为什么 简单明了的理由当然是开发效率高 但是学习 Python 的初学者往往会面临以下残酷的现状 网上充斥着大量的学习资源 书籍 视频教程和博客 但是大部分都是讲解基础知识 不够深入 也有的比较晦涩 难以理解
  • 这或许是最全的 Python 数据分析指南(全)

    因工作需求经常会面试一些数据分析师 一些 coding 能力很强的小伙伴 当被问及数据分析方法论时一脸懵逼的 或者理所当然的认为就是写代码啊 在文章开头先来解释一下数据分析 数据分析是通过明确分析目的 梳理并确定分析逻辑 针对性的收集 整理
  • Python爬虫入门(一)

    前言 很多人都或多或少听说过 Python 爬虫 我也一直很感兴趣 所以也花了一个下午入门了一下轻量级的爬虫 为啥是轻量级的爬虫呢 因为有的网页是比较复杂的 比如需要验证码 登录验证或者需要证书才能访问 我们了解爬虫的概念和架构 只需要做一
  • Python爬虫 (适合初学者)

    关于爬虫是什么 怎样保证爬虫的合法性小编在这就不再过多的阐述 从本章起 小编将和大家一起分享在学习python爬虫中的所学 希望可以和大家一起进步 也希望各位可以关注一下我 首先我们来初步了解下如何使用开发者工具进行抓包 以 https f
  • std::iota 函数简单使用

    std iota 是 C 标准库中的一个算法 位于
  • LeetCode-周赛-思维训练-中等难度

    第一题 1798 你能构造出连续值的最大数目 解题思路 我们先抛开原题不看 可以先完成一道简单的题目 假设现在就给你一个目标值X 问你能够构造出从 1 X 的连续整数 最小需要几个数 贪心假设 期望 我们要尽量用最少的数目 构造出最长的连续
  • Django系列之Celery异步框架+RabbitMQ使用

    在Django项目中 如何集成使用Celery框架来完成一些异步任务以及定时任务呢 1 安装 pip install celery celery框架 pip install django celery beat celery定时任务使用 p
  • LeetCode-数组-重叠、合并、覆盖问题-中等难度

    435 无重叠区间 我认为区间类的题型 大多数考验的是思维能力 以及编码能力 该类题型本身并无什么算法可言 主要是思维逻辑 比如本题实际上你只需要能够总结出重叠与不重叠的含义 再加上一点编码技巧 便可完成 解题思路 正如前面所说 那么解题的
  • python 的几种创建以及删除虚拟环境的方法

    在 Python 中 有几种工具可以用来创建和管理虚拟环境 其中两个最常用的是 venv 和 virtualenv 同时 也有一些第三方工具如 conda 用于 Conda 环境 和 pipenv 用于项目环境 1 使用 venv venv
  • 课程资料|组合数学-电子版教材!

    写在前面 学习成绩 不管是是保研 考研 出国留学 还是找实习找工作 都是你极为重要的支撑 为了帮助同学们 更好地学习专业课 斩获高绩点 岛主将持续推出 CS相关的专业教材资料 希望同学们能够在接下来的学习生活中取得良好的成绩 今天为大家带来
  • 1130 - Host “WIN-CA4FHERGO9J‘ is not allowed to connect to this MySQL server

    1 知识小课堂 1 1 Mysql MySQL是一个关系型数据库管理系统 由瑞典 MySQL AB 公司开发 属于 Oracle 旗下产品 它是最流行的关系型数据库管理系统之一 在WEB应用方面 MySQL是最好的RDBMS Relatio
  • 使用rknn-toolkit2将paddleseg模型导出rknn模型

    目录 安装paddle2onnx环境 将paddle模型导出onnx模型 安装rknn toolkits 转化rknn模型 安装paddle2onnx环境 首先创建一个python虚拟环境 conda create n paddle2onn
  • ThreadLocal 本地线程变量详解

    概述 ThreadLocal 意为本地线程变量 即该变量只属于当前线程 对其他线程隔离 我们知道 一个普通变量如果被多线程访问会存在存在线程安全问题 这时我们可以使用 Synchronize 来保证该变量某一时刻只能有一个线程访问 从而解决