前面的耗时统计工具类已经相对成熟,接下来我们做一个异步能力的增强
接上文的耗时统计工具类,对于TraceWatch
工具类的基本使用,都是基于同步的策略来的,如
// 无返回结果
traceWatch.cost(() -> xxx, "task1");
// 有返回结果
ans = traceWatch.cost(() -> { xxx; return xxx; }, "task1");
接下来我们考虑对它的能力进行增强,我们希望可以简单的指定需要统计的代码块是同步执行还是异步执行
比如
// 同步执行
traceWatch.sync(() -> xxx, "task1");
// 异步执行
traceWatch.async(() -> xxx, "task1");
// or
// 通过传参控制,false表示同步执行, true表示异步执行
traceWatch.cost(() -> xxx, "task1", false);
traceWatch.cost(() -> xxx, "task1", true);
1. 异步增强
1.1 通用的线程池工具类
既然是异步能力增强,支持代码块的异步调度,那么我们可以封装一个简单的线程池工具类,主要用于异步执行的线程池的创建
/**
* 异步工具类
*
* @author YiHui
* @date 2024/8/11
*/
public class AsyncUtil {
public static ExecutorService executorService;
static {
initExecutorService(Runtime.getRuntime().availableProcessors() * 2, 50);
}
public static void initExecutorService(int core, int max) {
// 异步工具类的默认线程池构建
max = Math.max(core, max);
ThreadFactory THREAD_FACTORY = new ThreadFactory() {
private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
private final AtomicInteger threadNumber = new AtomicInteger(1);
public Thread newThread(Runnable r) {
Thread thread = this.defaultFactory.newThread(r);
if (!thread.isDaemon()) {
thread.setDaemon(true);
}
thread.setName("trace-watch-dog-" + this.threadNumber.getAndIncrement());
return thread;
}
};
executorService = new ThreadPoolExecutor(core, max, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(), THREAD_FACTORY, new ThreadPoolExecutor.CallerRunsPolicy());
}
}
1.2 异步能力增强
当我们希望包装的代码块可以同步/异步执行时,首先是在创建对象的时候,指定一下线程池
public class TraceRecoder implements Closeable {
/**
* trace记录名
*/
private final String traceName;
/**
* 一个子任务的执行耗时
*/
private final Map<String, Long> cost;
/**
* 异步调度的线程池
*/
private final ExecutorService executorService;
/**
* 用于标记是否所有的任务执行完毕
* 执行完毕之后,不在支持继续添加记录
*/
private volatile boolean markExecuteOver;
public DefaultTraceRecoder() {
this(AsyncUtil.executorService, "TraceBridge");
}
public DefaultTraceRecoder(ExecutorService executorService, String task) {
this.traceName = task;
this.cost = new ConcurrentHashMap<>();
startRecord(task);
this.executorService = TtlExecutors.getTtlExecutorService(executorService);
this.markExecuteOver = false;
}
private void start(String name) {
if (markExecuteOver) {
// 所有任务执行完毕,不再新增
System.out.println("allTask ExecuteOver ignore:" + name);
return;
}
cost.put(name, System.currentTimeMillis());
}
private void end(String name) {
long now = System.currentTimeMillis();
long last = cost.getOrDefault(name, now);
if (last >= now / 1000) {
// 之前存储的是时间戳,因此我们需要更新成执行耗时 ms单位
cost.put(name, now - last);
}
}
}
接下来我们封装一下上面的start
, end
方法的使用姿势,与前面的工具类的实现,重要的区别在于返回
/**
* 封装一下执行业务逻辑,记录耗时时间
*
* @param run 执行的具体业务逻辑
* @param name 任务名
* @return
*/
private Runnable runWithTime(Runnable run, String name) {
return () -> {
start(name);
try {
run.run();
} finally {
end(name);
}
};
}
/**
* 封装一下执行业务逻辑,记录耗时时间
*
* @param call 执行的具体业务逻辑
* @param name 任务名
* @return 返回结果
*/
private <T> Supplier<T> supplyWithTime(Supplier<T> call, String name) {
return () -> {
start(name);
try {
return call.get();
} finally {
end(name);
}
};
}
为什么返回的是 Runnable/Supplier
?
这个就需要从同步/异步的调用方法来看为什么这么设计了
对于同步执行的场景,我们可以分别为有返回/无返回提供两个方法
/**
* 同步执行,待返回结果
*
* @param supplie
回复