ExecutorCompletionService(以下简称ECS)是java.util.concurrent
工具类,用来代理ExecutorService,以木桶效应。
本人测试在最差情况下
- 没用ECS的异步
- 用了ECS的异步
- 传统同步
import java.util.concurrent.*;
import java.time.*;
import java.util.*;
// import java.util.logging.*;
import java.util.function.*;
import java.io.*;
import java.text.*;
public class Main {
private static final Logger logger = Logger.getLogger();
public static void main(String[] args) throws Throwable {
ExecutorService es = Executors.newVirtualThreadPerTaskExecutor();j a
// 创建延时任务(返回值,耗时)
var tasks = List.of(delayed("最慢完成的", 5),
delayed("一般", 3),
delayed("最快完成的", 1));
final Logger logger = new Logger();
tryExpAsync(es, tasks, true, logger::info); // 没用
tryExpAsync(es, tasks, false, logger::info); // 用了
tryExp(tasks, logger::info); // 同步
es.close();
}
/**
* 自定义日志工具类。
*
* 这个日志工具类会输出当前时间,精细到秒数
* 用法:
* <pre>
* final Logger logger = Logger.getLogger();
* logger.info("Hello, world!");
* </pre>
*/
private static class Logger {
static final SimpleDateFormat sdf = new SimpleDateFormat("HH-mm-ss");
static Logger theLogger;
private Logger() {}
public static Logger getLogger() {
if (theLogger == null) {
synchronized (Logger.class) {
if (theLogger == null) {
theLogger = new Logger();
}
}
}
return theLogger;
}
public void info(String msg) {
System.out.println("[%s] %s".formatted(sdf.format(new Date()), msg));
}
}
// 计时
private static <V> void tryExpAsync(ExecutorService es,
List<Callable<V>> tasks,
boolean orderedExecution,
Consumer<V> action)
throws Exception {
logger.info("开始任务!!!");
var now = Instant.now(); // 开始计时
resolveAll(es, tasks, orderedExecution, action);
System.out.println(now.elapsed().toSeconds() + "秒");
}
private static <V> void tryExp(List<Callable<V>> tasks, Consumer<V> action)
throws Exception {
logger.info("开始同步任务");
var now = Instant.now();
for (Callable<V> task : tasks) {
V result = task.call();
if (result != null)
action.accept(result);
}
System.out.println(now.elapsed().toSeconds() + " 秒");
}
/**
*
* @param e 线程池
* @param tasks 要被线程池执行的任务集
* @param orderedExecution 如果为 {@code true},就会禁用 {@code ExecutorCompletionService}
* 这就意味着线程池的结果是按照
* @param action 传入一个消费器,用来操作线程池完成任务后的结果
*/
private static <V> void resolveAll(ExecutorService e,
Collection<Callable<V>> tasks,
boolean orderedExecution,
Consumer<V> action)
throws Exception
{
final int len = tasks.size();
if (orderedExecution) {
// 将所有的任务提交到线程池,然后返回其Futures
List<Future<V>> futures = tasks.stream().map(e::submit).toList();
for (int i = 0, n = len; i < len; ++i) // 这里不用加强for遍历,以提高性能
action.accept(futures.get(i).get()); // 返回 Future
} else {
var ecs = new ExecutorCompletionService<V>(e);
tasks.forEach(ecs::submit);
for (int i = len; i > 0; --i)
action.accept(ecs.take().get());
}
}
/**
* 创建一个延时任务,返回一个 Callable<V>
*
* @param result 要返回的结果
* @param timeout 延时返回 {@code result} 的时长
*/
private static <V> Callable<V> delayed(V result, long timeout) {
return (Callable<V>)
() -> { TimeUnit.SECONDS.sleep(timeout); return result; };
}
/**
* 计时工具类,用法如下:
* <pre>
* var now = Instant.now();
* // 耗时任务
* System.out.println(now.elapsed().toSeconds());
* </pre>
*/
private static class Instant {
Duration dur;
Instant(Duration durr) { dur = durr; }
Instant(long timeout) { this(Duration.ofMillis(timeout)); }
static Instant now() {
return new Instant(
Duration.ofMillis(System.currentTimeMillis()));
}
Instant elapsed() {
return new Instant(
Duration.ofMillis(System.currentTimeMillis() - dur.toMillis()));
}
long toSeconds() { return dur.toSeconds(); }
long toMillis() { return dur.toMillis(); }
long toHours() { return dur.toHours(); }
long toDays() { return dur.toDays(); }
}
}