
本文介绍在 Spring 应用中使用 OptaPlanner 的 SolverManager 通过回调监听器或异步结果获取机制,精准捕获求解结束时刻并安全获取最优解,避免轮询或阻塞等待。
本文介绍在 spring 应用中使用 optaplanner 的 `solvermanager` 通过回调监听器或异步结果获取机制,精准捕获求解结束时刻并安全获取最优解,避免轮询或阻塞等待。
在基于 Spring 的 OptaPlanner 应用中,求解器(Solver)通常以异步方式运行(尤其推荐使用 SolverManager 而非 Solver 原生实例),因此不能依赖同步阻塞调用判断结束。正确做法是利用 SolverManager 提供的声明式监听能力,在求解完成时自动触发业务逻辑。
✅ 推荐方案:使用 solve() 或 solveAndListen() 配置完成回调
SolverManager.solve(…) 和 solveAndListen(…) 均支持传入 Consumer
// 方式一:仅关心最终解(简洁常用)
solverManager.solve(
1L, // problemId(用于追踪/取消)
MyClass::getProblem, // 问题供应函数
finalBestSolution -> {
if (finalBestSolution != null) {
log.info("Optimization completed. Best score: {}",
finalBestSolution.getScore());
// ✅ 此处可安全保存结果、更新数据库、发送通知等
resultService.saveResult(finalBestSolution);
}
}
);// 方式二:同时监听中间解与最终解(适合需实时反馈的场景)
solverManager.solveAndListen(
1L,
MyClass::getProblem,
bestSolution -> {
// 可选:每获得新最佳解时触发(例如推送进度)
log.debug("New best solution found: {}", bestSolution.getScore());
},
finalBestSolution -> {
// 必须实现:求解真正结束时执行(含超时、终止、异常等情况)
handleFinalResult(finalBestSolution);
},
null // 使用默认异常处理器(自动记录 ERROR 日志)
);⚠️ 注意事项:
- finalBestSolution 可能为 null(如求解中途被取消或发生未捕获异常),务必判空;
- 所有回调均在 SolverManager 的内部线程池中执行,请勿在此做耗时 I/O 操作(如同步写库),建议委托给 @Async 方法或消息队列;
- problemId 是关键标识符,可用于后续调用 solverManager.terminateEarly(problemId) 主动停止。
✅ 备选方案:通过 SolverJob 同步/异步获取结果
若需更细粒度控制(如等待、超时、取消),可先获取 SolverJob 实例,再调用其 getFinalBestSolution()(该方法会阻塞直至求解完成):
SolverJob<MySolution, Long> job = solverManager.solve(1L, problem); // ⚠️ 注意:此行为是阻塞的,不适用于 Web 请求主线程! MySolution finalSolution = job.getFinalBestSolution(); // 返回 null 表示无有效解
更推荐结合 CompletableFuture 使用(SolverJob 实现了 CompletionStage):
solverManager.solve(1L, problem)
.toCompletableFuture()
.thenAccept(finalSolution -> {
if (finalSolution != null) {
// 在异步线程中处理结果
notificationService.notifySuccess(finalSolution);
}
})
.exceptionally(throwable -> {
log.error("Solver job failed", throwable);
return null;
});? 总结
- 永远优先使用 SolverManager + 回调监听器(solve() / solveAndListen()),这是 OptaPlanner 官方推荐的、线程安全且事件驱动的最佳实践;
- 避免手动轮询 Solver.isSolving() 或依赖 Thread.sleep() 等不可靠方式;
- 在 Spring 环境中,确保 SolverManager 已正确配置为 @Bean(通常由 OptaPlannerSpringConfig 自动装配),并启用 @EnableOptaPlanner;
- 所有业务侧对求解结果的消费逻辑,都应封装在 finalBestSolutionListener 或 CompletableFuture 回调中,保障响应及时性与系统稳定性。

















