有了ThreadLocal为什么要使用TransmittableThreadLocal

举报
KevinQ 发表于 2023/04/19 14:03:59 2023/04/19
【摘要】 先说结论TransmittableThreadLocal解决的是子线程读取父线程数据的问题。Child Thread Read Parent Thread Data Locally. 分步讲解最近在一个项目中发现有如下一行代码:// 包引用import com.alibaba.ttl.TransmittableThreadLocal;private static final ThreadL...

先说结论

TransmittableThreadLocal解决的是子线程读取父线程数据的问题。

Child Thread Read Parent Thread Data Locally.

分步讲解

最近在一个项目中发现有如下一行代码:

// 包引用
import com.alibaba.ttl.TransmittableThreadLocal;


private static final ThreadLocal<Long> TENANT_ID = new TransmittableThreadLocal<>();

当时就有一个疑问,为什么不用java自带的TheadLocal而要使用第三方的TransmittableThreadLocal呢?

换句话说,使用TransmittableThreadLocal是为了解决什么问题呢?

有时在线程中又创建了子线程,同时子线程有读取父线程数据的需求,就要用到这个类了,看样例:

看样例:

Maven中引入相关包:

<dependency>  
    <groupId>com.alibaba</groupId>  
    <artifactId>transmittable-thread-local</artifactId>  
    <version>2.12.2</version>  
</dependency>

使用Java自带的ThreadLocal:

package com.kevinq.myapp;  
  
/**  
 * @author qww  
 * 2023/4/18 14:45 */
 public class UserThreadLocal {  
  
    public void test() throws InterruptedException {  
  
        ThreadLocal<String> parentThreadLocal = new ThreadLocal<>();  
        parentThreadLocal.set("父线程中的数据xxxx");  
  
        Thread childThread = new Thread(() -> {  
            String parentThreadLocalValue = parentThreadLocal.get();  
            System.out.println("使用ThreadLocal,获取的父线程数据: " + parentThreadLocalValue);  
        });  
  
        childThread.start();  
        childThread.join();  
    }  
}

使用TransmittableThreadLocal

package com.kevinq.myapp;  
  
import com.alibaba.ttl.TransmittableThreadLocal;  
  
/**  
 * @author qww  
 * 2023/4/18 14:45 */
public class UseTransmittableThreadLocal {  
  
    public void test() throws InterruptedException {  
  
        ThreadLocal<String> parentThreadLocal = new TransmittableThreadLocal<>();  
        parentThreadLocal.set("父线程中的数据xxxx");  
  
        Thread childThread = new Thread(() -> {  
            String parentThreadLocalValue = parentThreadLocal.get();  
            System.out.println("使用TransmittableThreadLocal,获取的父线程数据:: " + parentThreadLocalValue);  
        });  
  
        childThread.start();  
        childThread.join();  
    }  
}

main方法启动测试:

package com.kevinq.myapp;  
  
/**  
 * @author qww  
 * 2023/4/18 14:43 */
public class TestMain {  
  
    public static void main(String[] args) throws InterruptedException {  
  
        UserThreadLocal t1 = new UserThreadLocal();  
        UseTransmittableThreadLocal t2 = new UseTransmittableThreadLocal();  
  
        t1.test();  
        t2.test();  
    }  
}

测试结果:

使用ThreadLocal,获取的父线程数据: null
使用TransmittableThreadLocal,获取的父线程数据:: 父线程中的数据xxxx

如上所示,使用ThreadLocal无法获取父线程的数据,而使用TrnsmittableThreadLocal就可以。

使用场景

异步编程场景下,保证线程池上下文的正确性,如多租户系统中,请求开始时,设置请求信息,租户信息,用户信息等。

原理描述

详细代码层面老实讲,看不太明白,看文字描述吧还是。

TransmittableThreadLocal 时,它实际上是通过 InheritableThreadLocal 和 WeakHashMap 实现线程上下文的传递。

在父线程中,我们将需要传递的变量存储在 TransmittableThreadLocal 中。

  1. 当子线程创建时,InheritableThreadLocal 会将父线程中的值复制一份传递给子线程。
  2. 接着,在子线程中,TransmittableThreadLocal 会将这些值存储在一个 WeakHashMap 中,WeakHashMap 的键是 Thread 对象,值是一个 ThreadLocalMapThreadLocalMap 用于存储 TransmittableThreadLocal 的值,因为 ThreadLocalMap 是线程私有的,所以不会出现线程安全的问题。
  3. 当子线程执行完毕后,WeakHashMap 会清理掉这个子线程对应的 ThreadLocalMap。这样可以避免出现内存泄漏的问题。

需要注意的是,TransmittableThreadLocal 需要在父线程中设置值后,再创建子线程。因为 InheritableThreadLocal 是在子线程创建时将父线程中的值复制一份,如果在子线程创建后再设置 TransmittableThreadLocal 的值,那么子线程中将无法获取到这些值。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。