父子线程之间传值
分布式链路追踪系统的时候,需要解决异步调用透传上下文的需求,比如传递traceId,典型场景例子:
- 分布式跟踪系统 或 全链路压测(即链路打标)
- 日志收集记录系统上下文
Session级Cache- 应用容器或上层框架跨应用代码给下层
SDK传递信息
JDK对跨线程传递ThreadLocal的支持
void testThreadLocal(){ ThreadLocal<Object> threadLocal = new ThreadLocal<>(); threadLocal.set("not ok"); new Thread(()->{ System.out.println(threadLocal.get()); }).start(); }
java中的threadlocal,是绑定在线程上的。你在一个线程中set的值,在另外一个线程是拿不到的。上面的输出是:null
InheritableThreadLocal
只是支持父子线程,线程池会有问题
InheritableThreadLocal<String> itl = new InheritableThreadLocal<>(); itl.set("father"); new Thread(()->{ System.out.println("subThread:" + itl.get()); itl.set("son"); System.out.println(itl.get()); }).start(); Thread.sleep(500);//等待子线程执行完 System.out.println("thread:" + itl.get());
输出是:
subThread:father //子线程可以拿到父线程的变量
son
thread:father //子线程修改不影响父线程的变量
JDK的InheritableThreadLocal类可以完成父线程到子线程的值传递。对于使用线程池等会池化复用线程的执行组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;
transmittable-thread-local
TransmittableThreadLocal继承InheritableThreadLocal,使用方式也类似。相比InheritableThreadLocal,添加了
copy方法
用于定制 任务提交给线程池时 的ThreadLocal值传递到 任务执行时 的拷贝行为,缺省传递的是引用。
注意:如果跨线程传递对象引用因为不再有线程封闭,与InheritableThreadLocal.childValue一样,使用者/业务逻辑要注意传递对象的线程protected的beforeExecute/afterExecute方法
执行任务(Runnable/Callable)的前/后的生命周期回调,缺省是空操作。
方式一:TtlRunnable封装:
ExecutorService executorService = Executors.newCachedThreadPool(); TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); // ===================================================== // 在父线程中设置 context.set("value-set-in-parent"); // 额外的处理,生成修饰了的对象ttlRunnable Runnable ttlRunnable = TtlRunnable.get(() -> { System.out.println(context.get()); }); executorService.submit(ttlRunnable);
方式二:ExecutorService封装:
ExecutorService executorService = ... // 额外的处理,生成修饰了的对象executorService executorService = TtlExecutors.getTtlExecutorService(executorService);
方式三:使用java agent,无代码入侵
实现线程池的传递是透明的,业务代码中没有修饰Runnable或是线程池的代码。即可以做到应用代码 无侵入。
ExecutorService executorService = Executors.newCachedThreadPool(); TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); // ===================================================== // 在父线程中设置 context.set("value-set-in-parent"); executorService.submit(() -> { System.out.println(context.get()); });
立志如山 静心求实
浙公网安备 33010602011771号