ScheduledExecutorService是Java中用于执行定时或周期性任务的首选工具,相比Timer更灵活、健壮。它基于线程池机制,支持并发执行任务,避免单线程导致的任务阻塞和异常崩溃问题。通过Executors工厂可创建单线程或线程池实例,核心调度方法包括:schedule()用于延迟执行一次任务;scheduleAtFixedRate()按固定频率周期执行,从任务开始时间计时;scheduleWithFixedDelay()则在任务结束后等待指定延迟再执行下一次,适用于需稳定间隔的场景。对于有返回值的任务,可使用Callable配合schedule()获取Future结果。关键优势在于异常隔离——单个任务异常不会影响其他任务调度,但周期性任务若未捕获异常会导致后续调度被取消,因此必须在任务内部使用try-catch处理异常。为增强容错,可自定义ThreadFactory并设置UncaughtExceptionHandler作为兜底。生命周期管理至关重要,应调用shutdown()停止接收新任务,并结合awaitTermination()等待任务完成;若超时,则调用shutdownNow()尝试中断正在运行的任务。完整关闭流程需兼顾优雅停机与强制终止,确保资源释放,防止程序无法退出。总之,ScheduledExecutorService在调度能力、并发支持和错误处理上全面优于Timer,是现代Java应用中定时任务的最佳选择。

Java中
ScheduledExecutorService
Timer
ScheduledExecutorService
使用
ScheduledExecutorService
Executors
newSingleThreadScheduledExecutor()
newScheduledThreadPool(int corePoolSize)
创建好实例之后,就可以用它提供的几种调度方法了:
schedule(Runnable command, long delay, TimeUnit unit)
Runnable
delay
立即学习“Java免费学习笔记(深入)”;
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.schedule(() -> {
System.out.println("这个任务在延迟5秒后执行了一次。");
}, 5, TimeUnit.SECONDS);
// 记得关闭,否则程序可能不会退出
// scheduler.shutdown(); // 通常在应用生命周期结束时调用schedule(Callable<V> callable, long delay, TimeUnit unit)
Callable
Future
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
Future<String> future = scheduler.schedule(() -> {
System.out.println("这个带返回值的任务在延迟3秒后执行。");
return "任务完成!";
}, 3, TimeUnit.SECONDS);
try {
System.out.println("任务结果: " + future.get()); // 阻塞直到任务完成并获取结果
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
period
period
period
period
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); // 使用线程池
System.out.println("开始执行 scheduleAtFixedRate 任务,当前时间:" + System.currentTimeMillis());
scheduler.scheduleAtFixedRate(() -> {
long startTime = System.currentTimeMillis();
System.out.println("scheduleAtFixedRate 任务执行开始,时间:" + startTime);
try {
Thread.sleep(2000); // 模拟任务执行2秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("scheduleAtFixedRate 任务执行结束,耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}, 1, 3, TimeUnit.SECONDS); // 首次延迟1秒,之后每3秒执行一次scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
delay
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
System.out.println("开始执行 scheduleWithFixedDelay 任务,当前时间:" + System.currentTimeMillis());
scheduler.scheduleWithFixedDelay(() -> {
long startTime = System.currentTimeMillis();
System.out.println("scheduleWithFixedDelay 任务执行开始,时间:" + startTime);
try {
Thread.sleep(2000); // 模拟任务执行2秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("scheduleWithFixedDelay 任务执行结束,耗时:" + (System.currentTimeMillis() - startTime) + "ms");
}, 1, 3, TimeUnit.SECONDS); // 首次延迟1秒,任务执行结束后再延迟3秒开始下一次在实际应用中,别忘了在程序退出或者不再需要定时任务时,调用
scheduler.shutdown()
ScheduledExecutorService
这个问题其实挺经典的,尤其是在一些老项目中,你可能会看到
java.util.Timer
ScheduledExecutorService
Timer
Timer
相比之下,
ScheduledExecutorService
Executor
ScheduledExecutorService
Future
Runnable
ScheduledExecutorService
Timer
ScheduledExecutorService
在使用
ScheduledExecutorService
Runnable
scheduleAtFixedRate
scheduleWithFixedDelay
那么,怎么处理这个问题呢?
最直接、最有效的策略就是:在你的任务代码内部,一定要做好异常捕获。把所有可能抛出异常的代码块都用
try-catch
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
try {
// 这里放你实际的任务逻辑
System.out.println("任务开始执行,当前时间: " + System.currentTimeMillis());
if (Math.random() > 0.7) { // 模拟偶尔出现异常
throw new RuntimeException("模拟任务执行失败!");
}
System.out.println("任务成功完成。");
} catch (Exception e) {
// 捕获所有可能的异常,并进行适当的处理,比如记录日志
System.err.println("定时任务执行异常: " + e.getMessage());
// 这里可以根据业务需求进行恢复操作,或者发送告警
}
}, 0, 5, TimeUnit.SECONDS); // 每5秒执行一次通过这种方式,即使任务内部出现异常,异常也会被捕获并处理,而不会“冒泡”到
ScheduledExecutorService
另外,如果你想更全面地处理线程池中所有线程的未捕获异常(不仅仅是定时任务),你可以考虑为
ScheduledExecutorService
ThreadFactory
UncaughtExceptionHandler
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger counter = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "ScheduledTask-" + counter.incrementAndGet());
t.setUncaughtExceptionHandler((thread, e) -> {
System.err.println("线程 [" + thread.getName() + "] 发生未捕获异常: " + e.getMessage());
// 这里可以做更高级的错误处理,比如重启服务或者发送通知
});
return t;
}
};
ScheduledExecutorService schedulerWithHandler = Executors.newScheduledThreadPool(1, threadFactory);
schedulerWithHandler.scheduleAtFixedRate(() -> {
System.out.println("任务执行中...");
if (Math.random() > 0.8) {
throw new RuntimeException("这个异常会被UncaughtExceptionHandler捕获!");
}
}, 0, 3, TimeUnit.SECONDS);需要注意的是,即使有了
UncaughtExceptionHandler
Runnable
UncaughtExceptionHandler
try-catch
管理
ScheduledExecutorService
优雅地关闭
ScheduledExecutorService
shutdown()
ScheduledExecutorService
shutdown()
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2); // 提交一些任务... // ... scheduler.shutdown(); // 启动关闭序列
awaitTermination(long timeout, TimeUnit unit)
shutdown()
awaitTermination()
timeout
try {
// 等待所有任务在60秒内完成
if (!scheduler.awaitTermination(60, TimeUnit.SECONDS)) {
System.err.println("线程池未能在指定时间内关闭。");
// 此时可以考虑强制关闭
} else {
System.out.println("线程池已优雅关闭。");
}
} catch (InterruptedException e) {
// 当前线程在等待过程中被中断
System.err.println("等待线程池关闭时被中断。");
Thread.currentThread().interrupt(); // 重新设置中断标志
}shutdownNow()
awaitTermination()
shutdownNow()
// 假设在awaitTermination超时后
List<Runnable> unexecutedTasks = scheduler.shutdownNow();
System.err.println("强制关闭线程池,有 " + unexecutedTasks.size() + " 个任务未执行。");
// 对未执行的任务进行处理,比如记录日志或重新安排一个完整的关闭流程通常是这样的:
public void shutdownScheduler(ScheduledExecutorService scheduler) {
scheduler.shutdown(); // 1. 发出关闭信号
try {
// 2. 等待一段时间,看任务能否自然完成
if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) {
System.err.println("定时任务线程池未在30秒内关闭,尝试强制关闭...");
// 3. 如果超时,强制关闭
scheduler.shutdownNow();
// 4. 再次等待,确保强制关闭成功
if (!scheduler.awaitTermination(30, TimeUnit.SECONDS)) {
System.err.println("定时任务线程池未能完全关闭。");
} else {
System.out.println("定时任务线程池已强制关闭。");
}
} else {
System.out.println("定时任务线程池已优雅关闭。");
}
} catch (InterruptedException ie) {
// 5. 如果当前线程在等待过程中被中断,也要强制关闭
System.err.println("关闭定时任务线程池时当前线程被中断,强制关闭...");
scheduler.shutdownNow();
Thread.currentThread().interrupt(); // 重新设置中断标志
}
}在使用
shutdownNow()
Thread.sleep()
wait()
join()
InterruptedException
catch
shutdownNow()
以上就是Java中ScheduledExecutorService定时任务使用的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号