前面的耗时统计工具类增加了异步能力的增强,此时我们需要考虑上下文信息的传递
前面完成的TraceRecoder
支持了异步代码块的调度,接下来我们就需要重点解决一下多线程下的数据传递问题,确保异步代码块的执行过程中,不会出现各种难以理解的并发问题
1. 并发问题复现
首先我们先来看一下,TraceRecoder
会在什么场景出现问题
1.1 上下文再线程池场景下的共享异常
既然我们的工具类是支持异步代码块封装,考虑到上下文的共享,我们第一想到就是使用InheritableThreadLocal
来替代 ThreadLocal
来存储上下文信息
但是有过了解的小伙伴会知道这个东西,在线程池的场景是可能出现共享异常的
我们可以构造一个简单的demo来验证一下
// 一个公共的随机休眠的方法
private Random random = new Random();
private void sleep(int max) {
try {
Thread.sleep(random.nextInt(max));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private ThreadLocal<String> local = new InheritableThreadLocal<>();
@Test
public void testConcurrent() throws InterruptedException {
// 外部链路执行线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
// TraceRecoder的异步线程池
ExecutorService traceExecutors = Executors.newFixedThreadPool(2);
executorService.submit(() -> {
local.set("t1");
try (TraceRecorder recorder = new TraceRecorder(traceExecutors, "t1")) {
recorder.async(() -> {
sleep(10);
System.out.println("1-1 上下文:" + Thread.currentThread().getName() + "_" + local.get());
}, "1-1");
recorder.async(() -> {
sleep(10);
System.out.println("1-2 上下文:" + Thread.currentThread().getName() + "_" + local.get());
}, "1-2");
recorder.async(() -> {
sleep(100);
System.out.println("1-3 上下文:" + Thread.currentThread().getName() + "_" + local.get());
}, "1-3");
recorder.sync(() -> sleep(100), "1-4同步");
} finally {
local.remove();
}
});
executorService.submit(() -> {
local.set("t2");
try (TraceRecorder recorder = new TraceRecorder(traceExecutors, "t2")) {
recorder.async(() -> {
sleep(100);
System.out.println("2-1 上下文:" + Thread.currentThread().getName() + "_" + local.get());
回复