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 //m.sbmmt.com/ All Rights Reserved | php.cn | 湘ICP备2023035733号