java自定义任务类定时执行任务示例 callable和future接口使用方法


下面是一个简单的Java示例,展示了如何使用`Callable`接口和`Future`接口来创建并定时执行一个自定义任务。在这个例子中,我们将使用`ScheduledExecutorService`来定时执行任务。

首先,我们定义一个实现了`Callable`接口的任务类。`Callable`接口类似于`Runnable`,但`Callable`可以返回一个结果,并且可以抛出异常。


import java.util.concurrent.Callable;

public class MyTask implements Callable<String> {
    private int taskId;

    public MyTask(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public String call() throws Exception {
        // 模拟任务执行,比如一些计算或数据处理
        Thread.sleep(1000); // 假设任务执行需要1秒
        return "Task " + taskId + " completed successfully";
    }
}

接下来,我们使用`ScheduledExecutorService`来定时执行这个任务。在这个例子中,我们将每隔3秒执行一次这个任务,并获取其执行结果。


import java.util.concurrent.*;

public class TaskScheduler {

    public static void main(String[] args) {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

        // 初始任务延迟0秒后开始执行,之后每隔3秒执行一次
        ScheduledFuture<?> future = scheduler.scheduleAtFixedRate(
            () -> {
                // 使用Callable需要配合FutureTask
                FutureTask<String> futureTask = new FutureTask<>(new MyTask(1)); // 假设taskId固定为1
                // 提交FutureTask到另一个线程池中执行(这里为了简单,我们使用同一个线程池,但通常不建议这么做)
                // 注意:在真实应用中,你可能需要另一个ExecutorService来执行这些FutureTask
                new Thread(futureTask).start();

                try {
                    // 获取任务执行结果
                    String result = futureTask.get(); // 这会阻塞当前线程直到任务完成
                    System.out.println(result);
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                }
            },
            0, // 初始延迟0秒
            3, // 每隔3秒执行一次
            TimeUnit.SECONDS
        );

        // 假设我们只想运行这个任务5次,然后关闭线程池
        scheduler.schedule(() -> scheduler.shutdown(), 15, TimeUnit.SECONDS);
    }
}

// 注意:上面的示例为了演示Callable和Future的使用,做了一些简化处理。
// 在实际开发中,你可能需要更复杂的逻辑来处理Future的获取,尤其是当你需要并行处理多个任务时。
// 另外,通常不建议在ScheduledExecutorService的定时任务中直接阻塞(如使用futureTask.get()),
// 这可能会导致定时任务无法按时执行或线程池耗尽。
// 一种更好的做法是,将Callable任务提交给另一个ExecutorService,并在需要时从Future中获取结果。

**注意**:上面的代码示例中,为了演示`Callable`和`Future`的使用,我直接在`scheduleAtFixedRate`的Runnable中启动了新线程来执行`FutureTask`。然而,这种做法并不推荐,因为它违背了`ScheduledExecutorService`设计的初衷,并且可能导致资源管理和错误处理的复杂性增加。在实际应用中,你应该将`Callable`任务提交给另一个`ExecutorService`来异步执行,并从返回的`Future`中获取结果。

此外,`futureTask.get()`是一个阻塞调用,它会阻塞当前线程直到任务完成。在定时任务中使用它可能会导致问题,因为定时任务期望的是快速返回以便进行下一次调度。如果你确实需要等待结果,请考虑使用其他机制(如回调函数、`CompletableFuture`等)来避免阻塞。