如何在线程间传递参数

前言

最近排查业务系统一个与跨线程共享数据(或者说传递数据)错误的问题,决定在这里梳理一下线程间数据传输的问题。跨线程传输数据的方式有很多,后续再写文章介绍,本次只对ThreadLocal及其衍生的类InheritableThreadLocal、TransmittableThreadLocal做一些使用的梳理与原理的分析。

应用场景

1.业务系统参数传递
假如你有一个web系统,每次访问客户端会有一些公共参数传递进来,然后你的不同的业务方法,都想使用,这个时候要么你做一层防腐层,将所有有用的参数封装一下接连往下传,这样每个用到的地方都要传递,另一种方式,就是我们要介绍的ThreadLocal的方式,你可以通过spring的拦截器,将所有的参数拦截下来,存在threadLocal里,后续用到的地方直接获取,但是你的各个模块儿,需要异步处理比如用到线程池,这个时候就需要跨线程间的传递。
2. 分布式跟踪系统
这里比如阿里的鹰眼、美团的cat、mtrace等等监控系统中,要打通各个系统之间的调用链路,他们就需要在每一次系统全链路调用中进行染色,将每一次全链路调用的数据关联在展示平台展示,便于业务工程师后续分析系统调用,链路调用之间的染色、标识都离不开线程间数据传递。
3.日志收集记录系统
同样无论异步打印,多线程内打印,均需要记录上文信息,此时也离不开线程间的数据传递。

ThreadLocal 仅仅是本线程间共享数据

看例子:

   ThreadLocal bxTL = new ThreadLocal<>();
        bxTL.set(1);
        System.out.println("----当前线程:"+  Thread.currentThread().getName() +"线程内数据为:" + bxTL.get());

        new Thread(() -> {
            System.out.println("----当前线程:"+  Thread.currentThread().getName() +"线程内数据为:" + bxTL.get());
        }).start();

        Thread.sleep(1000L);

结果如下:

----当前线程:main线程内数据为:1
----当前线程:Thread-0线程内数据为:null

InheritableThreadLocal 仅可以父子线程间共享数据

大家看下代码:

  ExecutorService executorService = Executors.newFixedThreadPool(1);

        executorService.submit(() ->{
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });


        InheritableThreadLocal bxTL = new InheritableThreadLocal<>();
        bxTL.set(1);
        System.out.println("----当前线程:"+  Thread.currentThread().getName() +"线程内数据为:" + bxTL.get());

        //创建子线程
        new Thread(() -> {
            System.out.println("----当前线程:"+  Thread.currentThread().getName() +"线程内数据为:" + bxTL.get());
        }).start();

        //线程池执行
        executorService.submit(()->{
            System.out.println("----当前线程:"+  Thread.currentThread().getName() +"线程内数据为:" + bxTL.get());
        });


        Thread.sleep(1000L);
  代码输出:
----当前线程:main线程内数据为:1
----当前线程:Thread-0线程内数据为:1
----当前线程:pool-1-thread-1线程内数据为:null

这里说明一下,new Thread的时候,是在赋值了ThreadLocal之后执行的,而线程池是在之前就创建了,且线程池中的线程在主线程设置threadlocal之前就启动了,所以线程池没办传递该共享变量。

阿里TransmittableThreadLocal跨线程传递

 ExecutorService executorService = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));

        executorService.submit(() ->{
            try {
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });


        TransmittableThreadLocal bxTL = new TransmittableThreadLocal<>();
        bxTL.set(1);
        System.out.println("----当前线程:"+  Thread.currentThread().getName() +"线程内数据为:" + bxTL.get());

        //创建子线程
        new Thread(() -> {
            System.out.println("----当前线程:"+  Thread.currentThread().getName() +"线程内数据为:" + bxTL.get());
        }).start();

        //线程池执行
        executorService.submit(()->{
            System.out.println("----当前线程:"+  Thread.currentThread().getName() +"线程内数据为:" + bxTL.get());
        });


        Thread.sleep(1000L);

执行结果

----当前线程:main线程内数据为:1
----当前线程:Thread-1线程内数据为:1
----当前线程:pool-1-thread-1线程内数据为:1

这里大家注意的是需要应用阿里封装的线程池和线程本地变量,原理下个章节跟大家分析,其实,他是在你放入任务的时候,将任务上带的数据传递给了新的线程池中的线程。

总结

这里跟大家展示不同线程间共享变量的场景及例子,下个章节,跟大家详述原理,希望大家有所收获。

你可能感兴趣的:(高效编程,微服务,缓存,java,thread,多线程,后端,面试)