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));
}

核心逻辑:

  1. runAsync()异步执行任务
  2. thenAccept()在任务完成后触发回调

⚠️ 注意:由于Runnable不返回结果,resultnull。如需处理返回值,应改用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类。它同时实现了RunnableFuture接口,可提交给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);
}

执行流程:

  1. 创建FutureTask实例
  2. 提交到单线程执行器
  3. 任务完成后自动调用done()方法

输出结果:

Task in progress
task complete: task details here

✅ 优点:可获取任务执行状态
❌ 缺点:需要额外维护ExecutorService

7. 方案对比

方案 适用场景 优点 缺点
实现Runnable 简单单次任务 实现直观 需手动管理线程
CompletableFuture 链式异步任务 代码简洁 无返回值时resultnull
扩展ThreadPoolExecutor 批量任务处理 线程池自动管理 配置较复杂
扩展FutureTask 需要任务状态管理 支持取消/状态查询 需配合ExecutorService使用

推荐选择

  • 简单场景 → CompletableFuture(一行代码搞定)
  • 高并发场景 → 扩展ThreadPoolExecutor(资源管理更专业)
  • 需要任务控制 → 扩展FutureTask(可查询/取消任务)

所有示例代码可在GitHub仓库获取。


原始标题:How to Get Notified When a Task Completes in Java Executors