java
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Supplier;
/**
* CompletableFuture并行任务超时处理模板
* 功能:
* 1. 单个任务独立超时控制 + 降级处理
* 2. 整体任务汇总超时控制
* 3. 自定义线程池避免资源耗尽
* 4. 异常统一处理
*/
public class CompletableFutureTemplate {
// 自定义线程池(核心参数可根据业务调整)
private static final ThreadPoolExecutor CUSTOM_EXECUTOR = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(), // 核心线程数(CPU核心数)
Runtime.getRuntime().availableProcessors() * 2, // 最大线程数
60, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(100), // 任务队列(避免无限扩容)
new ThreadFactory() { // 线程命名(便于问题排查)
private int count = 0;
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("parallel-task-" + count++);
thread.setDaemon(false); // 非守护线程,避免主线程退出后被强制终止
return thread;
}
},
new ThreadPoolExecutor.CallerRunsPolicy() // 队列满时,让提交任务的线程执行(限流+避免任务丢失)
);
/**
* 并行执行多个任务,支持单个任务超时和整体超时
* @return 汇总后的业务结果
*/
public BusinessResult executeParallelTasks() {
try {
// 1. 定义并行任务(可根据业务增减)
CompletableFuture<Task1Result> task1 = wrapWithTimeout(
this::doTask1, // 任务1:实际业务逻辑
300, TimeUnit.MILLISECONDS, // 超时时间
this::getTask1Fallback // 超时/异常时的降级数据
);
CompletableFuture<Task2Result> task2 = wrapWithTimeout(
this::doTask2, // 任务2:易超时的业务(如房源推荐)
500, TimeUnit.MILLISECONDS, // 超时时间可适当延长
this::getTask2Fallback
);
CompletableFuture<Task3Result> task3 = wrapWithTimeout(
this::doTask3, // 任务3:快速任务
200, TimeUnit.MILLISECONDS,
this::getTask3Fallback
);
// 2. 等待所有任务完成(整体超时控制)
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2, task3);
// 整体超时时间:取单个任务超时的最大值 + 冗余时间(避免单个任务接近超时导致整体超时)
allTasks.get(800, TimeUnit.MILLISECONDS);
// 3. 汇总结果(join()不会抛出受检异常,因已通过wrapWithTimeout处理)
return new BusinessResult(
task1.join(),
task2.join(),
task3.join()
);
} catch (TimeoutException e) {
// 整体超时:返回更基础的降级结果(如只保留核心数据)
log.warn("整体任务超时,返回基础降级结果");
return getOverallFallback();
} catch (Exception e) {
// 其他异常(如线程池满):返回应急降级结果
log.error("并行任务执行异常", e);
return getEmergencyFallback();
}
}
/**
* 包装任务:添加超时控制和降级处理
* @param task 实际任务
* @param timeout 超时时间
* @param unit 时间单位
* @param fallback 降级策略
* @param <T> 任务返回类型
* @return 包装后的CompletableFuture
*/
private <T> CompletableFuture<T> wrapWithTimeout(
Supplier<T> task,
long timeout,
TimeUnit unit,
Supplier<T> fallback) {
return CompletableFuture.supplyAsync(() -> {
try {
// 执行实际任务(若任务支持中断,可在此处处理中断逻辑)
return task.get();
} catch (Exception e) {
// 任务执行中抛出异常:直接降级
log.error("任务执行异常,触发降级", e);
return fallback.get();
}
}, CUSTOM_EXECUTOR)
// 超时后直接返回降级数据(无需抛异常)
.completeOnTimeout(fallback.get(), timeout, unit)
// 额外处理可能的异常(如线程池拒绝)
.exceptionally(ex -> {
log.error("任务超时或被中断,触发降级", ex);
return fallback.get();
});
}
// ------------------------------
// 以下为业务相关方法(需根据实际场景实现)
// ------------------------------
/** 任务1:示例业务(如获取用户信息) */
private Task1Result doTask1() {
// 实际业务逻辑:如调用用户服务API
return new Task1Result("user123", "张三");
}
/** 任务1降级数据 */
private Task1Result getTask1Fallback() {
// 降级策略:返回默认用户信息或缓存数据
return new Task1Result("default", "匿名用户");
}
/** 任务2:示例易超时业务(如房源推荐) */
private Task2Result doTask2() {
// 实际业务逻辑:如调用推荐服务API(可能耗时较长)
return new Task2Result(List.of("house_101", "house_102"));
}
/** 任务2降级数据 */
private Task2Result getTask2Fallback() {
// 降级策略:返回热门房源缓存
return new Task2Result(List.of("house_hot_01", "house_hot_02"));
}
/** 任务3:示例快速业务(如获取价格趋势) */
private Task3Result doTask3() {
// 实际业务逻辑:如查询缓存中的价格趋势
return new Task3Result(10000.0, 10500.0); // 昨日均价,今日均价
}
/** 任务3降级数据 */
private Task3Result getTask3Fallback() {
// 降级策略:返回默认趋势或空数据
return new Task3Result(0.0, 0.0);
}
/** 整体超时降级结果 */
private BusinessResult getOverallFallback() {
// 策略:只保留核心任务的降级数据
return new BusinessResult(
getTask1Fallback(), // 核心任务:用户信息不能丢
getTask2Fallback(),
new Task3Result(null, null) // 非核心任务可返回空
);
}
/** 应急降级结果(极端异常时) */
private BusinessResult getEmergencyFallback() {
// 策略:只返回最基础的可用数据
return new BusinessResult(
new Task1Result("emergency", "系统维护中"),
new Task2Result(List.of()),
new Task3Result(null, null)
);
}
// ------------------------------
// 以下为业务模型类(示例)
// ------------------------------
static class BusinessResult {
private Task1Result userInfo;
private Task2Result houseRecommend;
private Task3Result priceTrend;
public BusinessResult(Task1Result userInfo, Task2Result houseRecommend, Task3Result priceTrend) {
this.userInfo = userInfo;
this.houseRecommend = houseRecommend;
this.priceTrend = priceTrend;
}
// getters/setters
}
static class Task1Result {
private String userId;
private String userName;
// 构造器、getters/setters
public Task1Result(String userId, String userName) {
this.userId = userId;
this.userName = userName;
}
}
static class Task2Result {
private List<String> houseIds;
// 构造器、getters/setters
public Task2Result(List<String> houseIds) {
this.houseIds = houseIds;
}
}
static class Task3Result {
private Double yesterdayPrice;
private Double todayPrice;
// 构造器、getters/setters
public Task3Result(Double yesterdayPrice, Double todayPrice) {
this.yesterdayPrice = yesterdayPrice;
this.todayPrice = todayPrice;
}
}
// 日志工具(实际项目中替换为SLF4J等)
private static void log(String msg) {
System.out.println("[INFO] " + msg);
}
private static void log(String msg, Throwable e) {
System.err.println("[ERROR] " + msg + ":" + e.getMessage());
}
}
模板核心特性说明
- 分层超时控制
- 单个任务超时:通过
completeOnTimeout
为每个任务设置独立超时时间(如任务2设500ms,适合房源推荐等耗时不稳定的场景) - 整体任务超时:通过
allTasks.get(800ms)
控制总耗时,避免单个任务接近超时导致整体响应变慢
- 单个任务超时:通过
- 多级降级策略
- 单任务降级:每个任务超时/异常时返回预设降级数据(如房源推荐超时返回热门房源缓存)
- 整体降级:整体超时时返回核心数据(如只保留用户信息,舍弃非核心的价格趋势)
- 应急降级:极端异常(如线程池满)时返回最基础可用数据,保证接口不报错
- 线程池安全管理
- 自定义线程池:避免使用默认
ForkJoinPool
导致的资源耗尽问题 - 线程命名:便于日志排查具体任务的问题
- 拒绝策略:采用
CallerRunsPolicy
,让提交任务的线程执行,既限流又避免任务丢失
- 自定义线程池:避免使用默认
- 异常全面处理
- 任务执行异常:通过
try-catch
捕获并降级 - 超时异常:通过
completeOnTimeout
和exceptionally
双重保障 - 整体异常:统一捕获并返回应急结果
- 任务执行异常:通过
使用时的调整建议
- 超时时间设置
- 单个任务:根据历史耗时95分位值设置(如95%的请求在300ms内完成,则设300ms)
- 整体超时:单个任务最大超时 + 20%冗余(避免网络波动导致整体超时)
- 降级数据设计
- 核心任务(如用户信息):降级数据需可用(如缓存的历史信息)
- 非核心任务(如推荐列表):可返回空或默认数据,但需保证前端兼容
- 线程池参数
- 核心线程数:CPU核心数(IO密集型任务可适当增加)
- 队列大小:根据并发量设置(如支持100并发,则队列设100)
- 中断支持
- 若任务涉及IO操作(如HTTP请求、数据库查询),建议使用支持中断的API(如
CloseableHttpClient
),超时后可主动释放资源
- 若任务涉及IO操作(如HTTP请求、数据库查询),建议使用支持中断的API(如
通过此模板,可有效避免单个任务超时阻塞整体接口,同时保证系统在异常情况下的可用性和稳定性。