栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

精通Java并发编程N+5:TransmittableThreadLocal解决线程池中ThreadLocal无法在父子线程之间传递的问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

精通Java并发编程N+5:TransmittableThreadLocal解决线程池中ThreadLocal无法在父子线程之间传递的问题

一、前言

在上一篇博文(https://blog.csdn.net/Saintmm/article/details/124461915)中介绍了InheritableThreadLocal如何实现父子线程之间传递ThreadLocal信息;最后提到InheritableThreadLocal的一个问题:在使用线程池时InheritableThreadLocal会完全失效。

二、TransmittableThreadLocal

因为线程池中的线程是会被复用的,所以在使用线程池进行多线程编程时,操作线程本地变量时需要特别注意;下面我们使用TransmittableThreadLocal 解决线程本地变量在线程池之间的传递问题。

1、问题复现

1> 复现案例:
创建一个核心线程数为2,最大线程数为2的线程池,在主线程中每次对线程本地变量赋值之后,提交一个线程任务到线程池中运行,如下:

public class InheritableThreadLocalTest1 {

    public final static InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal<>();
    public static final int CORE_THREAD_SIZE = 2;

    public static ThreadPoolExecutor executorService = new ThreadPoolExecutor(CORE_THREAD_SIZE,
            CORE_THREAD_SIZE,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>());

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

        inheritableThreadLocal.set("main thread-1");
        executorService.submit(() -> {
            System.out.println("1 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get());
        });

        inheritableThreadLocal.set("main thread-2");
        executorService.submit(() -> {
            System.out.println("2 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get());
        });

        inheritableThreadLocal.set("main thread-3");
        executorService.submit(() -> {
            System.out.println("3 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get());
        });

        inheritableThreadLocal.set("main thread-4");
        executorService.submit(() -> {
            System.out.println("4 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get());
        });

    }
}

2> 运行结果如下:

3> 结果分析:从运行结果结合线程池的运行机制可以看出:

  1. 第一任务提交到线程池时,线程池中没有核心线程,创建一个线程,此时可以获取到父线程(main线程)中最新的InheritableThreadLocal值;
  2. 第二个任务提交到线程池时,线程池中核心线程数为1,1 < 2,所以再创建一个线程,此时也可以获取到父线程(main线程)中最新的InheritableThreadLocal值;
  3. 第三个任务提交到线程池时,线程池中核心线程数为2,线程进入到阻塞队列,等待空闲线程来调度任务。假如此时coreThread1刚执行完任务1,来执行任务3,则此时的InheritableThreadLocal是历史版本的(main thread-1)。
  4. 第四个任务提交到线程池时,线程池中核心线程数为2,线程进入到阻塞队列,等待空闲线程来调度任务。假如此时coreThread2刚执行完任务2,来执行任务4,则此时的InheritableThreadLocal是历史版本的(main thread-2)。

因此,我们可以确定,只有当核心线程数不足时才会正确的传递的InheritableThreadLocal,而如果线程池中的线程在循环使用了,则不会加载最新的InheritableThreadLocal,而是使用历史上的某个版本数据。

线程池部分内容请见博文:源码分析ThreadPoolExecutor线程池实现原理。

2、解决方案

引入阿里提供的TransmittableThreadLocal,github地址为:https://github.com/alibaba/transmittable-thread-local。

1)引入maven依赖

    com.alibaba
    transmittable-thread-local
    2.12.6

2)代码中使用

1> 修改线程本地变量类型为TransmittableThreadLocal

public final static TransmittableThreadLocal inheritableThreadLocal = new TransmittableThreadLocal<>();

其次,官方给定了三种方式保证TransmittableThreadLocal的正确传递:

2.1> 修饰Runnable和Callable
2.2> TtlExecutors 修饰线程池
2.3> 使用Java Agent来修饰JDK线程池实现类

2.1> 修饰Runnable和Callable:
public class TransmittableThreadLocalTest {

    public final static TransmittableThreadLocal inheritableThreadLocal = new TransmittableThreadLocal<>();
    public static final int CORE_THREAD_SIZE = 2;

    public static ThreadPoolExecutor executorService = new ThreadPoolExecutor(CORE_THREAD_SIZE,
            CORE_THREAD_SIZE,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>());

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

        inheritableThreadLocal.set("main thread-1");
        executorService.submit(TtlRunnable.get(
                        () -> System.out.println("1 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get())
                )
        );

        inheritableThreadLocal.set("main thread-2");
        executorService.submit(TtlRunnable.get(
                        () -> System.out.println("2 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get())
                )
        );

        inheritableThreadLocal.set("main thread-3");
        executorService.submit(TtlRunnable.get(
                        () -> System.out.println("3 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get())
                )
        );

        inheritableThreadLocal.set("main thread-4");
        executorService.submit(TtlRunnable.get(
                        () -> System.out.println("4 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get())
                )
        );

    }
}

运行结果正常,如下所示:

2.2> TtlExecutors 修饰线程池:
public class TransmittableThreadLocalTest {

    public final static TransmittableThreadLocal inheritableThreadLocal = new TransmittableThreadLocal<>();
    public static final int CORE_THREAD_SIZE = 2;

    public static ExecutorService executorService =
            TtlExecutors.getTtlExecutorService(new ThreadPoolExecutor(CORE_THREAD_SIZE,
                    CORE_THREAD_SIZE,
                    10,
                    TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>())
            );

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

        inheritableThreadLocal.set("main thread-1");
        executorService.submit(
                () -> System.out.println("1 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get())
        );

        inheritableThreadLocal.set("main thread-2");
        executorService.submit(
                () -> System.out.println("2 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get())
        );

        inheritableThreadLocal.set("main thread-3");
        executorService.submit(
                () -> System.out.println("3 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get())
        );

        inheritableThreadLocal.set("main thread-4");
        executorService.submit(
                () -> System.out.println("4 obtain inheritableThreadLocal in threadPool: " + inheritableThreadLocal.get())
        );

    }
}

运行结果和上述一致;

2.3> 使用Java Agent来修饰JDK线程池实现类;

该种方式我们在后续分析TransmittableThreadLocal的实现原理之后,再做演示、推论。

可以先参考:github <–> 23-使用java-agent来修饰jdk线程池实现类。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/856706.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号