FantasticMao 技术笔记
BlogGitHub
  • README
  • C & Unix
    • C
      • 《C 程序设计语言》笔记
      • C 语言中的陷阱
      • CMake 示例
      • GNU make
      • LLVM Clang
      • Nginx 常用模块
      • Vim 常用命令
    • Unix-like
      • 《深入理解计算机系统》笔记
      • 《UNIX 环境高级编程》笔记 - UNIX 基础知识
      • 《UNIX 环境高级编程》笔记 - 文件 IO
      • 《UNIX 环境高级编程》笔记 - 标准 IO 库
      • 《鳥哥的 Linux 私房菜》笔记 - 目录配置
      • 《鳥哥的 Linux 私房菜》笔记 - 认识与学习 bash
      • 《鳥哥的 Linux 私房菜》笔记 - 任务管理
      • OpenWrt 中的陷阱
      • iptables 工作机制
  • Go
    • 《A Tour of Go》笔记
    • Go vs C vsJava
    • Go 常用命令
    • Go 语言中的陷阱
  • Java
    • JDK
      • 《Java 并发编程实战》笔记 - 线程池的使用
      • 设计模式概览
      • 集合概览
      • HashMap 内部算法
      • ThreadLocal 工作机制
      • Java Agent
    • JVM
      • 《深入理解 Java 虚拟机》笔记 - Java 内存模型与线程
      • JVM 运行时数据区
      • 类加载机制
      • 垃圾回收算法
      • 引用类型
      • 垃圾收集算法
      • 垃圾收集器
    • Spring
      • Spring IoC 容器扩展点
      • Spring Transaction 声明式事务管理
      • Spring Web MVC DispatcherServlet 工作机制
      • Spring Security Servlet 实现原理
    • 其它
      • 《Netty - One Framework to rule them all》演讲笔记
      • Hystrix 设计与实现
  • JavaScript
    • 《写给大家看的设计书》笔记 - 设计原则
    • 《JavaScript 权威指南》笔记 - jQuery 类库
  • 数据库
    • ElasticSearch
      • ElasticSearch 概览
    • HBase
      • HBase 数据模型
    • Prometheus
      • Prometheus 概览
      • Prometheus 数据模型和指标类型
      • Prometheus 查询语法
      • Prometheus 存储原理
      • Prometheus vs InfluxDB
    • Redis
      • 《Redis 设计与实现》笔记 - 简单动态字符串
      • 《Redis 设计与实现》笔记 - 链表
      • 《Redis 设计与实现》笔记 - 字典
      • 《Redis 设计与实现》笔记 - 跳跃表
      • 《Redis 设计与实现》笔记 - 整数集合
      • 《Redis 设计与实现》笔记 - 压缩列表
      • 《Redis 设计与实现》笔记 - 对象
      • Redis 内存回收策略
      • Redis 实现分布式锁
      • Redis 持久化机制
      • Redis 数据分片方案
      • 使用缓存的常见问题
    • MySQL
      • 《高性能 MySQL》笔记 - Schema 与数据类型优化
      • 《高性能 MySQL》笔记 - 创建高性能的索引
      • 《MySQL Reference Manual》笔记 - InnoDB 和 ACID 模型
      • 《MySQL Reference Manual》笔记 - InnoDB 多版本
      • 《MySQL Reference Manual》笔记 - InnoDB 锁
      • 《MySQL Reference Manual》笔记 - InnoDB 事务模型
      • B-Tree 简述
      • 理解查询执行计划
  • 中间件
    • gRPC
      • gRPC 负载均衡
    • ZooKeeper
      • ZooKeeper 数据模型
    • 消息队列
      • 消息积压解决策略
      • RocketMQ 架构设计
      • RocketMQ 功能特性
      • RocketMQ 消息存储
  • 分布式系统
    • 《凤凰架构》笔记
    • 系统设计思路
    • 系统优化思路
    • 分布式事务协议:二阶段提交和三阶段提交
    • 分布式系统的技术栈
    • 分布式系统的弹性设计
    • 单点登录解决方案
    • 容错,高可用和灾备
  • 数据结构和算法
    • 一致性哈希
    • 布隆过滤器
    • 散列表
  • 网络协议
    • 诊断工具
    • TCP 协议
      • TCP 报文结构
      • TCP 连接管理
由 GitBook 提供支持
在本页
  • 实现原理
  • 一图概览
  • 内存泄漏
  • 在父子线程之间传递数据
  • 向可复用的线程传递数据
  1. Java
  2. JDK

ThreadLocal 工作机制

使用线程封闭是实现线程安全最简单的方式之一,维持线程封闭性的一种常规方法是使用 ThreadLocal,它能使线程中的某个值与保存值的对象关联起来。ThreadLocal 提供了 get 与 set 等方法,这些方法为每个使用该变量的线程都有一份独立的副本,因此 get 总是返回由当前执行线程在调用 set 时设置的最新值。

实现原理

Thread 在内部定义了一个 ThreadLocal.ThreadLocalMap 类型的 threadLocals 变量,用于在 ThreadLocal 中获取当前线程关联的 ThreadLocal.ThreadLocalMap 实例。

public class Thread implements Runnable {
	// ......

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

    // ......
}

ThreadLocal.ThreadLocalMap 在内部定义了一个 ThreadLocal.ThreadLocalMap.Entry 数组 table 变量。ThreadLocal.ThreadLocalMap.Entry 继承了 WeakReference<ThreadLocal<?>>,它使用 ThreadLocal 实例的引用作为 Map 的 key,使用 ThreadLocal set 方法设置的数据作为 Map 的 value。

public class ThreadLocal<T> {
    // ......

	static class ThreadLocalMap {

		static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;

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

        private Entry[] table;

        private void set(ThreadLocal<?> key, Object value) {
            Entry[] tab = table;
            // ......
            tab[i] = new Entry(key, value);
            // ......
        }

        // ......
    }

    // ......
}

ThreadLocal 在对 ThreadLocal.ThreadLocalMap 进行封装之后,对外提供了 get 和 set 方法。

public class ThreadLocal<T> {
    // ......

	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();
    }

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

    // ......
}

一图概览

+------------+
|Thread      |
|------------|   +--------------------------+
|threadLocals|-->|ThreadLocal.ThreadLocalMap|
|------------|   |--------------------------|   +--------------------------------+
| ......     |   |table[]                   |-->|ThreadLocal.ThreadLocalMap.Entry|
+------------+   |--------------------------|   |-----------+-----------+--------|
                 | ......                   |   |ThreadLocal|ThreadLocal|        |
                 +--------------------------+   |  WeakRef  |  WeakRef  | ...... |
                                                |-----------+-----------+--------|
                                                |   value   |   value   | ...... |
                                                +-----------+-----------+--------+

内存泄漏

Thread 和 ThreadLocal 中都会持有 ThreadLocal.ThreadLocalMap 的引用,并且 ThreadLocal 内部与 ThreadLocal.ThreadLocalMap 的引用关系是 WeakReference 弱引用。所以在 ThreadLocal 已经被 GC 回收,并且当 Thread 还存活时,ThreadLocal.ThreadLocalMap 就是一个只会占用内存但不会被实际使用的内存垃圾。不过当 Thread 运行结束时,ThreadLocal.ThreadLocalMap 也会被回收,此时也就不存在内存泄漏的问题了。

在父子线程之间传递数据

InheritableThreadLocal 扩展了 ThreadLocal,支持在创建子线程时继承父线程中保存的 InheritableThreadLocal 数据。

向可复用的线程传递数据

最后更新于1年前

ThreadLocal 中的数据与线程的生命周期绑定在一起,因此不适用于使用线程池等会池化复用线程的场景。但是如果应用确实使用了线程池 ThreadPoolExecutor,并且存在将 任务提交时 的数据传递给 任务执行时 的需求时候,可以考虑使用阿里巴巴的开源组件:。

TransmittableThreadLocal