返回顶部
分享到

阿里巴巴TransmittableThreadLocal使用介绍

java 来源:互联网 作者:佚名 发布时间:2025-02-15 23:07:56 人浏览
摘要

ThreadLocal在上下文的数据传输上非常的方便和简洁。 工业实践中,比较常用的有三个,ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal,那么他们三个之间有什么区别呢? 常见的三种ThreadLoc

ThreadLocal在上下文的数据传输上非常的方便和简洁。

工业实践中,比较常用的有三个,ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal,那么他们三个之间有什么区别呢?

常见的三种ThreadLocal比较

  ThreadLocal InheritableThreadLocal TransmittableThreadLocal
来源 jdk jdk 阿里开源
单线程数据传输 支持 支持 支持
new线程数据传输 不支持 支持 支持
线程池数据传输 不支持 部分支持【简单场景】 支持

针对线程池的数据传输,InheritableThreadLocal仅仅能在一些简单场景下做到,下面就用一个案例来说明

  • 先看下线程工厂的定义

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

package com.tml.mouseDemo.core.threadLocalDemo;

 

import java.util.concurrent.ThreadFactory;

import java.util.concurrent.atomic.AtomicInteger;

 

public class SimpleThreadFactory implements ThreadFactory {

 

    private static AtomicInteger atomicInteger = new AtomicInteger(1);

    @Override

    public Thread newThread(Runnable r) {

 

        Thread t = new Thread(r);

        t.setName("tml-"+atomicInteger.getAndIncrement());

        return t;

    }

}

  • InheritableThreadLocal部分支持的案例

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

package com.tml.mouseDemo.core.threadLocalDemo;

 

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class ThreadLocalDemo1 {

 

    private static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

 

    private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());

 

 

    public static void main(String[] args) throws InterruptedException {

 

 

        threadLocal.set("hello main");

        for (int i = 0; i < 2; i++) {

            service.execute(() -> {

                String s = threadLocal.get();

                ThreadUtils.printLog("get data " + s);

 

            });

        }

 

        //修改threadLocal中的值

        threadLocal.set("hello world");

        Thread.sleep(2000);

        for (int i = 0; i < 2; i++) {

 

            service.execute(() -> {

                String s = threadLocal.get();

                ThreadUtils.printLog("get data " + s);

 

            });

        }

 

 

        ThreadUtils.printLog("get data " + threadLocal.get());

 

        service.shutdown();

 

    }

}

  • 运行结果如下

2025-01-10 19:41:50 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:50 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:52 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:41:52 | INFO | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:41:52 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main

从运行结果来看,前面的两次循环提交的任务,在子线程中确实是能正常的获取主线程设置的变量,即hello main,但是紧接着,我修改了主线程上绑定的变量为hello world,然后继续循环两次提交两个任务,这个时候子线程中获取的线程变量依然是hello main,这明显是与是期望不一致的。

从这个层面来讲,InheritableThreadLocal确实在线程池的层面支持不够友好,可以说仅支持部分简单场景。

根本原因就死线程池的池化机制,从上面的运行日志上也可以看出,提交了4个任务执行的线程依然是两个。线程池中的线程是复用的,InheritableThreadLocal是在创建子线程的时候,会将主线程上绑定的数据拷贝过来,如果我不创建新的线程,但是主线程上绑定的数据改变了呢?那我依然还是读取到之前拷贝的数据。

这个就是InheritableThreadLocal的短板。针对这个问题,阿里开源的TransmittableThreadLocal就能顺利丝滑的解决这个问题。

TransmittableThreadLocal实践

maven依赖

?

1

2

3

4

5

<dependency>

     <groupId>com.alibaba</groupId>

    <artifactId>transmittable-thread-local</artifactId>

     <version>2.12.4</version>

</dependency>

装饰Runnable任务

  • 直接看代码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

package com.tml.mouseDemo.core.threadLocalDemo;

 

import com.alibaba.ttl.TransmittableThreadLocal;

import com.alibaba.ttl.TtlRunnable;

 

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class ThreadLocalDemo1 {

 

    private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();

 

    private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());

 

 

    public static void main(String[] args) throws InterruptedException {

 

 

        threadLocal.set("hello main");

        for (int i = 0; i < 2; i++) {

            service.execute(TtlRunnable.get(() -> {

                String s = threadLocal.get();

                ThreadUtils.printLog("get data " + s);

 

            }));

        }

 

        //修改threadLocal中的值

        threadLocal.set("hello world");

        Thread.sleep(2000);

        for (int i = 0; i < 2; i++) {

 

            service.execute(TtlRunnable.get(() -> {

                String s = threadLocal.get();

                ThreadUtils.printLog("get data " + s);

 

            }));

        }

 

 

        ThreadUtils.printLog("get data " + threadLocal.get());

 

        service.shutdown();

 

    }

}

  • 运行结果如下:

2025-01-10 19:57:03 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:57:03 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 19:57:05 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:57:05 | INFO | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 19:57:05 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world

与第一个案例的差异点在于,使用了

?

1

2

3

public static TtlRunnable get(@Nullable Runnable runnable) {

    return get(runnable, false, false);

}

来增强了了Runnable任务,执行的结果也是符合预期。

装饰线程池

  • 直接看代码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

package com.tml.mouseDemo.core.threadLocalDemo;

 

import com.alibaba.ttl.TransmittableThreadLocal;

import com.alibaba.ttl.TtlRunnable;

import com.alibaba.ttl.threadpool.TtlExecutors;

 

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class ThreadLocalDemo1 {

 

    private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();

 

    private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());

 

    private static ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(service);

 

 

 

    public static void main(String[] args) throws InterruptedException {

 

 

 

        threadLocal.set("hello main");

        for (int i = 0; i < 2; i++) {

            ttlExecutorService.execute(() -> {

                String s = threadLocal.get();

                ThreadUtils.printLog("get data " + s);

 

            });

        }

 

        //修改threadLocal中的值

        threadLocal.set("hello world");

        Thread.sleep(2000);

        for (int i = 0; i < 2; i++) {

 

            ttlExecutorService.execute(() -> {

                String s = threadLocal.get();

                ThreadUtils.printLog("get data " + s);

 

            });

        }

 

 

        ThreadUtils.printLog("get data " + threadLocal.get());

 

        service.shutdown();

 

    }

}

  • 运行结果如下

2025-01-10 20:05:05 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:05:05 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:05:07 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:05:07 | INFO | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:05:07 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world

与上一个案例的差异点在于,这里没有包装Runnable任务,而是包装了线程池,使用了

?

1

2

3

4

5

6

public static ExecutorService getTtlExecutorService(@Nullable ExecutorService executorService) {

    if (TtlAgent.isTtlAgentLoaded() || executorService == null || executorService instanceof TtlEnhanced) {

        return executorService;

    }

    return new ExecutorServiceTtlWrapper(executorService, true);

}

包装了ExecutorService,执行结果也是符合预期

使用java Agent无侵入增强线程池

  • 直接看代码

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

package com.tml.mouseDemo.core.threadLocalDemo;

 

import com.alibaba.ttl.TransmittableThreadLocal;

 

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

public class ThreadLocalDemo1 {

 

    private static ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();

 

    private static ExecutorService service = Executors.newFixedThreadPool(2, new SimpleThreadFactory());

 

    public static void main(String[] args) throws InterruptedException {

 

 

 

        threadLocal.set("hello main");

        for (int i = 0; i < 2; i++) {

            service.execute(() -> {

                String s = threadLocal.get();

                ThreadUtils.printLog("get data " + s);

 

            });

        }

 

        //修改threadLocal中的值

        threadLocal.set("hello world");

        Thread.sleep(2000);

        for (int i = 0; i < 2; i++) {

 

            service.execute(() -> {

                String s = threadLocal.get();

                ThreadUtils.printLog("get data " + s);

 

            });

        }

 

 

        ThreadUtils.printLog("get data " + threadLocal.get());

 

        service.shutdown();

 

    }

}

项目运行的时候,需要添加额外的jvm启动参数,如下

  • 运行结果如下,也是符合预期

2025-01-10 20:11:59 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:11:59 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello main
2025-01-10 20:12:01 | INFO | tml-2 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:12:01 | INFO | main | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world
2025-01-10 20:12:01 | INFO | tml-1 | com.tml.mouseDemo.core.threadLocalDemo.ThreadUtils | get data hello world

总结

阿里巴巴的TransmittableThreadLocal是继承自InheritableThreadLocal,对他的功能进行了增强,增强的点也主要是在线程池的支持上。

通过上面的三个案例,可以看到TransmittableThreadLocal是非常灵活的,大家可以根据自己的需要,选择对应的方式来实现。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。

您可能感兴趣的文章 :

原文链接 :
相关文章
  • Java中实现订单超时自动取消功能(最新推荐)

    Java中实现订单超时自动取消功能(最新推荐)
    在开发中,我们会遇到需要延时任务的业务场景,例如:用户下单之后未在规定的时间内支付成功,该订单会自动取消; 用户注册成功15分
  • 阿里巴巴TransmittableThreadLocal使用介绍
    ThreadLocal在上下文的数据传输上非常的方便和简洁。 工业实践中,比较常用的有三个,ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal,那
  • SpringBoot使用Jackson介绍
    概述 Springboot配置JackSon处理类属性,JavaBean序列化为JSON格式,常用框架:阿里fastjson,谷歌gson、Jackson等。 ① 性能:Jackson Fastjson Gson 同个结
  • springboot结合JWT实现单点登录的代码

    springboot结合JWT实现单点登录的代码
    JWT实现单点登录 登录流程: 校验用户名密码-生成随机JWT Token-返回给前端。之后前端发请求携带该Token就能验证是哪个用户了。 校验流程:
  • java正则表达式匹配Matcher类的使用
    Matcher类 用法 在 Java 中,Matcher类是用于匹配正则表达式的工具,而group()方法是Matcher类中的一个重要方法,用于提取匹配结果中的捕获组(
  • Java基础面试真题:String为什么是不可变的?

    Java基础面试真题:String为什么是不可变的?
    + 目录 今天来分享一道群友去阿里云面试遇到的 Java 基础面试真题:String、StringBuffer、StringBuilder的区别?String为什么是不可变的?。 网站很
  • 在Spring Boot Web应用程序中序列化枚举

    在Spring Boot Web应用程序中序列化枚举
    枚举类型在定义应用程序域内有限且明确的值集方面非常有效,有助于避免代码中出现无效状态。 应用场景 以下以一个Spring Boot 3.3.x 和 M
  • Item记录线程安全的介绍

    Item记录线程安全的介绍
    确保并发访问安全:线程安全记录的必要性 类如何处理并发访问对于其使用者至关重要,这应被视为类契约的一部分。 错误地假设线程安全
  • 软件开发中的DTO和DAO的介绍

    软件开发中的DTO和DAO的介绍
    DTO(数据传输对象)和 DAO(数据访问对象)是软件架构中的两种常用模式,尤其在基于 Java 的应用中发挥着重要作用。它们协同工作,有效
  • 怎么使用Micronaut注释进行封装?

    怎么使用Micronaut注释进行封装?
    这种封装设计有效防止了对userId和name的直接修改,确保只有授权操作才能更改其值。 Micronaut框架的实际应用 Micronaut是一个基于JVM的现代微
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计