ThreadLocal及阿里(TransmittableThreadLocal,TTL)分析

news2024/11/13 16:21:27

TTL类关系图
ThreadLocal <- InheritableThreadLocal <- TransmittableThreadLocal

在这里插入图片描述

1. ThreadLocal

ThreadLocal 类提供线程本地(局部)变量。每个线程都有自己独立初始化的变量副本。
TheadLocal 允许我们存储仅由特定线程访问的数据,从而起到线程隔离的作用,避免了并发场景下的线程安全问题。

1.1 使用场景

  • 线程持有自己的数据变量
/**
 * 每个线程拥有自己的Integer变量,默认初始化为0
 */
private static final ThreadLocal<Integer> INTEGER_THREAD_LOCAL = ThreadLocal.withInitial(() -> 0);
  • 避免数据竞争,每个线程持有自己的线程变量
/**
 * 每个线程拥有自己的SDF,避免竞争,保证线程安全
 */
private static final ThreadLocal<SimpleDateFormat> SDF_TL = ThreadLocal.withInitial(() -> {
    return new SimpleDateFormat("yyyyMMddHHmmss");
});
  • 数据传递,跨方法级别数据传递
/**
 * 数据传递上下文: LogbackMDCAdapter MDC 实际功能实现
 */
final ThreadLocal<Map<String, String>> context = new ThreadLocal<>();

1.2 ThreadLocal主要方法

// get() 获取线程本地变量,如果没有,则调用setInitialValue() 初始化并设置
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();
}
// 初始化
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}
// 设置线程本地变量
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
// 移除线程本地变量
public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

1.3 实现原理

ThreadLocal实现主要依赖
1.ThreadLocal实例对象,作为线程访问数据的KEY存在。
2.ThreadLocalMap对象,每个线程有自己的map,ThreadLocal实例作为KEY。
注意:此处entry中的ThreadLocal key使用弱引用,防止内存泄漏,在清除方法中会清除所有 key为null的entry。

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        /** The value associated with this ThreadLocal. */
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
}

ThreadLoclMap作为每个线程实例的字段存储在线程实例中:

public class Thread implements Runnable {
    /**
     * 与此线程相关的ThreadLocal值。此Map由 ThreadLocal 类维护。
     */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    /**
     * 与此线程相关的 InheritableThreadLocal 值。此映射由 InheritableThreadLocal 类维护
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}

1.4 缺陷

ThreadLocal在创建子线程时不会把线程本地变量拷贝到子线程中,这就会导致子线程无法获取到本地线程保存的线程信息

2. InheritableThreadLocal

InheritableThreadLocal 可继承的ThreadLocal,继承并扩扩展了原始ThreadLocal的功能,提供从父线程到子线程的value继承:创建子线程时,子线程接收父线程具有的 inheritable thread-local 值。

2.1 使用场景

  • 新建子线程需要集成父线程中的线程本地变量时,可以使用 InheritableThreadLocal 实现
/**
 * 可继承ThreadLocal,子线程将继承父线程中ITL变量value
 */
private static final ThreadLocal<Integer> BIZ_ITL = new InheritableThreadLocal();

/**
 * 运行结果:
 * main-thread-get:123
 * sub-thread-get:123
 */
public static void main(String[] args) {
    BIZ_ITL.set(123);
    System.out.println("main-thread-get:"+BIZ_ITL.get());
    new Thread(()->{
        System.out.println("sub-thread-get:"+BIZ_ITL.get());
    }).start();
}

2.2 InheritableThreadLocal 主要方法

  • getMap() createMap() 使用 ITLMap ,非 TLMap
ThreadLocalMap getMap(Thread t) {
   return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
    t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}

2.3 实现原理

main线程调用get方法跳转到InheritableThreadLocal重写的createMap,让inheritableThreadLocals赋初值;后续http请求会copy main线程中的数据,同样赋予inheritableThreadLocals 值。
子程继承父线程 线程本地变量值 是在Thread创建时,copy父类现成中的 ITLMap中的key和Value:
Thread构造函数:调用init方法,在init方法中子线程根据父线程的ITLMap创建自己的ITLMap(传递、继承)。

// 初始化Thread
public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
    // 获取父线程
    Thread parent = currentThread();
    // 省略其他步骤
    if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}

2.4 缺陷

使用线程池时,线程不会每次都创建,也就不会进行父子线程之间的数据传递;如果使用池化的线程,就不会进行父子线程本地变量的拷贝。

3. TransmittableThreadLocal

3.1 使用场景

ThreadLocal和InheritableThreadLocal 能够完成变量的线程本地化和父子线程中的value传递。
但是现实项目中大多数线程池化在线程池中,因此,提交任务的线程无法将 提交现成的本地变量传递给执行task的任务线程。
TTL组件功能:在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。
业务期望:上下文生命周期的操作从业务逻辑中分离出来。业务逻辑不涉及生命周期,就不会有业务代码如疏忽清理而引发的问题了。

3.2 TransmittableThreadLocal主要方法

整个上下文的传递流程或说生命周期可以规范化成:
捕捉、回放和恢复这3个操作,即CRR(capture/replay/restore)模式。
CRR :

  1. capture方法:抓取线程(线程A)的所有TTL值。
  2. replay方法:在另一个线程(线程B)中,回放在capture方法中抓取的TTL值,并返回回放前TTL值的备份
  3. restore方法:恢复线程B执行replay方法之前的TTL值(即备份)
// 实现代码示例 TtlRunnable.class
// 初始化TtlRunnable对象
private TtlRunnable(@NonNull Runnable runnable, boolean releaseTtlValueReferenceAfterRun) {
	// TransmittableThreadLocal.Transmitter.capture() 抓取当前线程中的TTL值的备份
    this.capturedRef = new AtomicReference<>(capture());
    this.runnable = runnable;
    this.releaseTtlValueReferenceAfterRun = releaseTtlValueReferenceAfterRun;
}

@Override
public void run() {
	// 取出TTL值备份数据
    final Object captured = capturedRef.get();
    if (captured == null || releaseTtlValueReferenceAfterRun && !capturedRef.compareAndSet(captured, null)) {
        throw new IllegalStateException("TTL value reference is released after run!");
    }
    // 回放在capture方法中抓取的TTL值并返回回放前TTL值的备份
    final Object backup = replay(captured);
    try {
        runnable.run();
    } finally {
        // 恢复线程B执行replay方法之前的TTL值(即备份)
        restore(backup);
    }
}

3.3 使用样例

private static final int DEFAULT_SIZE = 8;
private static final TransmittableThreadLocal<Map<String, Object>> CACHE = new TransmittableThreadLocal<Map<String, Object>>() {
    @Override
    protected Map<String, Object> initialValue() {
        return new HashMap<>(DEFAULT_SIZE);
    }
};

Executor ttlExecutor = TtlExecutors.getTtlExecutor(new ThreadPoolExecutor(
       properties.getCoreSize(),
        properties.getMaxSize(),
        properties.getKeepAliveSeconds(),
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(),
        new ThreadPoolExecutor.CallerRunsPolicy()
));

ttlExecutor.execute(TtlRunnable.get(() -> {})); 

参考资料
阿里TTL(TransmittableThreadLocal)分析

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

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

相关文章

JavaWeb后端开发(第一期):Maven基础、Maven的安装配置、如何创建maven项目模块、maven的生命周期

Java后端开发&#xff1a;2024年2月6日 -> LiuJinTao 文章目录 JavaWeb后端开发&#xff08;第一期&#xff09; &#xff1a; maven基础一、 maven介绍1.1 什么maven呢&#xff1a;1.2 maven的作用1.3 maven 模型1.4 maven 仓库 二、maven 安装2.1 配置本地仓库2.2 配置阿里…

c#cad 创建-多线段(三)

运行环境 vs2022 c# cad2016 调试成功 一、程序说明 AutoCAD中创建多段线的。具体解释如下&#xff1a; 获取当前文档和数据库&#xff0c;并创建一个编辑器&#xff08;用于与用户交互&#xff09;。使用事务处理的方式&#xff0c;开始对数据库的操作。打开模型空间&…

LeetCode-第171题-Excel表的序列号

1.题目描述 给你一个字符串 columnTitle &#xff0c;表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 例如&#xff1a; A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 ... 2.样例描述 3.思路描述 遍历时将每个字母与 A 做减法&…

【开源】SpringBoot框架开发厦门旅游电子商务预订系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 景点类型模块2.2 景点档案模块2.3 酒店管理模块2.4 美食管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 学生表3.2.2 学生表3.2.3 学生表3.2.4 学生表 四、系统展示五、核心代码5.1 新增景点类型5.2 查询推荐的…

Linux——进程间通信:管道

我们在开发过程中&#xff0c;可能会碰到两个或多个进程需要协同进行&#xff0c;这两个进 程之间有着一定的关系&#xff0c;这个进程可能会需要另一个进程的某些消息来达 到自己的目的&#xff0c;或者是一个进程控制着另一个进程&#xff0c;又或者是需要某种资 源的共享。但…

速度规划:s形曲线应用(变速 停车)opencv c++显示(3)

理论篇 先看该篇&#xff0c;这里沿用了里面的变量。 应用推导篇 分为变速和停车两部分&#xff08;字迹潦草&#xff0c;可结合代码看&#xff09; 代码篇 变速函数入口&#xff1a; velocityPlanner vp; vp.SetParameters(0, 1);停车函数入口&#xff1a; ParkingVelo…

挑战杯 python+大数据校园卡数据分析

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于yolov5的深度学习车牌识别系统实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&am…

23.HarmonyOS App(JAVA)堆叠布局StackLayout使用方法

不常用 StackLayout直接在屏幕上开辟出一块空白的区域&#xff0c;添加到这个布局中的视图都是以层叠的方式显示&#xff0c;而它会把这些视图默认放到这块区域的左上角&#xff0c;第一个添加到布局中的视图显示在最底层&#xff0c;最后一个被放在最顶层。上一层的视图会覆盖…

mysql入门到精通005-基础篇-约束

1、概述 1.1 概念 约束是作用于表中字段上的规则&#xff0c;用于限制储存在表中的数据。 1.2 目的 保证数据库中数据的正确性、有效性和完整性。 1.3 常见的约束分类 一旦谈到外键&#xff0c;则至少涉及2张表约束是作用于表中字段上的&#xff0c;可以在创建表/修改表的…

机器学习-梯度下降法

不是一个机器学习算法是一种基于搜索的最优化方法作用&#xff1a;最小化一个损失函数梯度上升法&#xff1a;最大化一个效用函数 并不是所有函数都有唯一的极值点 解决方法&#xff1a; 多次运行&#xff0c;随机化初始点梯度下降法的初始点也是一个超参数 代码演示 impor…

【语音合成】中文-多情感领域-16k-多发音人

模型介绍 语音合成-中文-多情感领域-16k-多发音人 框架描述 拼接法和参数法是两种Text-To-Speech(TTS)技术路线。近年来参数TTS系统获得了广泛的应用&#xff0c;故此处仅涉及参数法。 参数TTS系统可分为两大模块&#xff1a;前端和后端。 前端包含文本正则、分词、多音字预…

在angular12中proxy.conf.json中配置详解

一、proxy.conf.json文件的目录 二、proxy.conf.json文件中的配置 "/xxx/api": {"target": "地址/api","secure": false,"logLevel": "debug","changeOrigin": true,"pathRewrite": {"…

【华为 ICT HCIA eNSP 习题汇总】——题目集13

1、以下在项目规划阶段中需要完成的工作是&#xff08;&#xff09;。 A、确定技术方案 B、了解项目背景 C、选择网络产品 D、规划 IP 地址 考点&#xff1a;网络规划与设计 解析&#xff1a;&#xff08;B&#xff09; 确定技术方案是在网络规划的设计阶段完成的工作&#xff…

团队管理-如何组织好一场会议

一、不同维度分析 1、按照时间维度 可分为 会前、会中、会后 会前 1、确定会议时间 尽可能选择参与者都空闲的时间&#xff0c;确保参与者都有时间可以参加&#xff0c;可以提前询问大家有空的时间&#xff0c;如果部分人没有时间但是会议比较紧急&#xff0c;可以让其选择…

2024:AI 大冒险

2024&#xff1a;AI 大冒险 2023 年就像一场疯狂的过山车&#xff0c;现在让我们一起系好安全带&#xff0c;来预测一下 2024 年的五大惊心动魄事件吧&#xff01; 一、AI 惹祸升级 嘿&#xff0c;2024 年可要小心了&#xff01;AI 这家伙可能会变得更调皮捣蛋。人们可能会用…

Bootstrap5 导航组件和面包屑

Bootstrap5 导航组件和面包屑 Bootstrap5 提供了一种简单快捷的方法来创建基本导航&#xff0c;它提供了非常灵活和优雅的选项卡和Pills等组件。 Bootstrap5 的所有导航组件&#xff0c;包括选项卡和Pills&#xff0c;都通过基本的 .nav 类共享相同的基本标记和样式。 使用 B…

springboot162基于SpringBoot的体育馆管理系统的设计与实现

体育馆管理系统 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本体育馆管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕…

搜索引擎DuckDuckGo代理指南

DuckDuckGo作為一款搜索引擎&#xff0c;同時擁有自己的流覽器&#xff0c;高度保護用戶隱私&#xff0c;使其有別於其他收集和利用用戶數據進行定向廣告的搜索引擎。然而&#xff0c;單獨使用DuckDuckGo並不能保證線上完全匿名。如果你想進一步保護隱私&#xff0c;那就需要使…

深度解析源码,Spring 如何使用三级缓存解决循环依赖

目录 一. 前言 二. 基础知识 2.1. 什么是循环依赖&#xff1f; 2.2. 三级缓存 2.3. 原理执行流程 三. 源码解读 3.1. 代码入口 3.2. 第一层 3.3. 第二层 3.4. 第三层 3.5. 返回第二层 3.6. 返回第一层 四. 原理深度解读 4.1. 什么要有三级缓存&#xff1f; 4.2.…

深入解析 Spring 事务机制

当构建复杂的企业级应用程序时&#xff0c;数据一致性和可靠性是至关重要的。Spring 框架提供了强大而灵活的事务管理机制&#xff0c;成为开发者处理事务的首选工具。本文将深入探讨 Spring 事务的使用和原理&#xff0c;为大家提供全面的了解和实际应用的指导。 本文概览 首…