1. 概述
Java提供了多种异步执行任务的方案,比如使用Executors。实际开发中,我们常需要知道任务何时完成,例如通知用户或启动下一个任务。本文将根据不同的任务执行方式,探讨获取任务完成通知的几种方案。
2. 基础准备
首先定义一个待执行任务和回调接口。任务实现Runnable
接口,这是Java中用于线程执行的标准接口:
class Task implements Runnable{
@Override
public void run() {
System.out.println("Task in progress");
// 业务逻辑写在这里
}
}
接着创建回调接口,包含一个接收字符串参数的方法。使用接口能让我们更灵活地实现不同场景的回调逻辑:
interface CallbackInterface {
void taskDone(String details);
}
实现该接口:
class Callback implements CallbackInterface {
void taskDone(String details){
System.out.println("task complete: " + details);
// 通知/告警逻辑写在这里
}
}
这些基础组件将在后续所有方案中复用,确保我们始终执行相同任务并接收相同的回调通知。
3. 实现Runnable
接口
最简单的方案是直接实现Runnable
接口。通过构造函数传入任务和回调,在run()
方法中依次执行:
class RunnableImpl implements Runnable {
Runnable task;
CallbackInterface callback;
String taskDoneMessage;
RunnableImpl(Runnable task, CallbackInterface callback, String taskDoneMessage) {
this.task = task;
this.callback = callback;
this.taskDoneMessage = taskDoneMessage;
}
void run() {
task.run();
callback.taskDone(taskDoneMessage);
}
}
使用示例:
@Test
void whenImplementingRunnable_thenReceiveNotificationOfCompletedTask() {
Task task = new Task();
Callback callback = new Callback();
RunnableImpl runnableImpl = new RunnableImpl(task, callback, "ready for next task");
runnableImpl.run();
}
输出结果符合预期:
Task in progress
task complete ready for next task
✅ 优点:实现简单直接
❌ 缺点:需要手动管理线程执行
4. 使用CompletableFuture
Java 8引入的CompletableFuture
可能是最简洁的方案。它实现了Future
接口,支持链式调用任务:
@Test
void whenUsingCompletableFuture_thenReceiveNotificationOfCompletedTask() {
Task task = new Task();
Callback callback = new Callback();
CompletableFuture.runAsync(task)
.thenAccept(result -> callback.taskDone("completion details: " + result));
}
核心逻辑:
runAsync()
异步执行任务thenAccept()
在任务完成后触发回调
⚠️ 注意:由于Runnable
不返回结果,result
为null
。如需处理返回值,应改用Supplier
接口。
5. 扩展ThreadPoolExecutor
当需要批量提交任务时,ThreadPoolExecutor
是更合适的选择。我们可以扩展它并重写afterExecute()
方法:
class AlertingThreadPoolExecutor extends ThreadPoolExecutor {
CallbackInterface callback;
public AlertingThreadPoolExecutor(CallbackInterface callback) {
super(1, 1, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
this.callback = callback;
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
callback.taskDone("runnable details here");
}
}
关键点:
- 构造函数中配置了单线程池(核心/最大线程数=1)
- 空闲线程超时60秒
- 任务队列容量为10
- 每个任务完成后自动触发回调
输出示例:
Task in progress
task complete runnable details here
✅ 优点:适合高并发场景
⚠️ 注意:需根据实际负载调整线程池参数
6. 扩展FutureTask
最后一种方案是扩展FutureTask
类。它同时实现了Runnable
和Future
接口,可提交给ExecutorService
执行:
class AlertingFutureTask extends FutureTask<String> {
CallbackInterface callback;
public AlertingFutureTask(Runnable runnable, Callback callback) {
super(runnable, null);
this.callback = callback;
}
@Override
protected void done() {
callback.taskDone("alert alert");
}
}
使用方式:
@Test
void whenUsingFutureTask_thenReceiveNotificationOfCompletedTask(){
Task task = new Task();
Callback callback = new Callback();
FutureTask<String> future = new AlertingFutureTask(task, callback);
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(future);
}
执行流程:
- 创建
FutureTask
实例 - 提交到单线程执行器
- 任务完成后自动调用
done()
方法
输出结果:
Task in progress
task complete: task details here
✅ 优点:可获取任务执行状态
❌ 缺点:需要额外维护ExecutorService
7. 方案对比
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
实现Runnable |
简单单次任务 | 实现直观 | 需手动管理线程 |
CompletableFuture |
链式异步任务 | 代码简洁 | 无返回值时result 为null |
扩展ThreadPoolExecutor |
批量任务处理 | 线程池自动管理 | 配置较复杂 |
扩展FutureTask |
需要任务状态管理 | 支持取消/状态查询 | 需配合ExecutorService 使用 |
推荐选择:
- 简单场景 →
CompletableFuture
(一行代码搞定) - 高并发场景 → 扩展
ThreadPoolExecutor
(资源管理更专业) - 需要任务控制 → 扩展
FutureTask
(可查询/取消任务)
所有示例代码可在GitHub仓库获取。