重写done()方法是合适的回调入口,因为它是FutureTask提供的受保护钩子方法,在任务结束(成功、异常或取消)后由执行线程自动且仅调用一次,相比轮询或阻塞get()更高效及时。

Java中可以通过继承FutureTask并重写done()方法,在任务完成(正常结束、异常或被取消)时触发回调逻辑。
为什么重写done方法是合适的回调入口
done()是FutureTask提供的受保护钩子方法,它在任务执行结束(无论成功、失败或取消)后由执行线程自动调用,且保证只执行一次。相比轮询isDone()或使用get()阻塞等待,这种方式更高效、响应及时,且无需额外线程干预。
继承FutureTask并重写done方法
定义子类,覆写done(),并在其中获取结果或异常:
- 调用
isCancelled()判断是否被取消 - 若未取消,调用
get()安全获取结果(此时不会阻塞,因任务已结束) - 捕获
ExecutionException处理业务异常,CancellationException处理取消情况
示例代码:
立即学习“Java免费学习笔记(深入)”;
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
class CallbackFutureTask<V> extends FutureTask<V> {
private final Consumer<V> onSuccess;
private final Consumer<Throwable> onError;
public CallbackFutureTask(Callable<V> callable,
Consumer<V> onSuccess,
Consumer<Throwable> onError) {
super(callable);
this.onSuccess = onSuccess;
this.onError = onError;
}
@Override
protected void done() {
try {
if (isCancelled()) {
onError.accept(new CancellationException());
} else {
V result = get(); // 此刻不会阻塞
onSuccess.accept(result);
}
} catch (CancellationException e) {
onError.accept(e);
} catch (ExecutionException e) {
onError.accept(e.getCause()); // 包装异常的原始原因
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
onError.accept(e);
}
}
}
在Executor中提交并触发回调
将自定义FutureTask提交给线程池,任务执行完毕后done()自动回调:
- 构造时传入
Callable和两个回调函数(成功/失败) - 调用
executor.execute(task)或submit(task)(注意:submit会包装一层,推荐用execute) - 无需手动调用
get(),避免阻塞主线程
使用示例:
ExecutorService executor = Executors.newFixedThreadPool(2);
CallbackFutureTask<String> task = new CallbackFutureTask<>(
() -> {
Thread.sleep(1000);
return "task completed";
},
result -> System.out.println("Success: " + result),
error -> System.err.println("Failed: " + error.getMessage())
);
executor.execute(task); // 启动任务,done将在后台自动调用
注意事项与常见陷阱
done()在任务执行线程中被调用(不是提交线程),需注意回调逻辑的线程安全性:
- 避免在
done()中执行耗时操作,否则会阻塞工作线程 - 如需UI更新或跨线程通知,应将回调分发到专用线程(如SwingUtilities.invokeLater、Handler.post等)
-
get()在done()中调用是安全的,但仅限此处;外部仍应避免重复调用 - 不要在
done()中再次调用cancel()或修改任务状态
不复杂但容易忽略

















