java并发系列-Java 中怎样延迟代码执行

1 介绍

Java 程序在操作中加入延迟或者暂停比较常见。这有助于任务调整节奏或者暂停执行直到另一个任务完成。

这篇教程描述 Java 中两种延迟的实现方式。

2 基于线程的方法

当 Java 程序运行时,它会在宿主机上创建一个进程。这个进程包含至少一个线程,即主线程,程序跑在里面。而且,Java 支持 multithreading,应用可以创建新的线程,与主线程并发执行,或者异步。

2.1 使用 Thread.sleep

Java 中快速和粗糙的方式去暂停是让当前线程 sleep 指定时间。这可以通过使用 Thread.sleep(milliseconds) 来做。

1
2
3
4
5
try {
Thread.sleep(secondsToSleep * 1000);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}

比较好的实践是将 sleep 封装在 try/catch 块中以防止另外一个线程中断 sleeping 的线程。这个案例中,我们捕获 InterruptedException 并显式地中断了当前线程,因此它后续可以被捕获和处理。这在多线程程序中更重要,但是在单线程程序中仍然不失为一个好的实践以免后续我们增加了其他线程。

2.2 使用 TimeUnit.sleep

为了更好的可读性,我们使用 TimeUnit.XXX.sleep(y)*,其中 *XXX 表示 sleep 的时间单位(SECONDS,MINUTES等等),y 是 sleep 的时间长度。下面是 TimeUnit 语法的使用例子:

1
2
3
4
5
try {
TimeUnit.SECONDS.sleep(secondsToSleep);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}

然而,这些基于线程的方法有一些缺点:

  • sleep 时间实际上不准确,特别是使用更小的时间单位的时候,比如 milliseconds 和 nanosecondes。
  • 当在循环中使用时,sleep 会在循环迭代中因此其他代码执行而略微偏移,因此执行时间在多次迭代后会不准确。

3 基于 ExecutorService 的方法

Java 提供了 ScheduledExecutorService 接口类,它是更加健壮和精确的方案。这个接口能够调度代码在指定延迟后执行一次或者按固定时间间隔执行。

每隔一定延迟执行一次代码,使用 schedule 方法:

1
2
3
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

executorService.schedule(Classname::someTask, delayInSeconds, TimeUnit.SECONDS);

Classname::someTask 部分用于指定在具体延迟后要执行的方法:

  • someTask 是要执行的方法的名字
  • Classname 是含有 someTask 方法的类名

每个固定时间间隔运行任务,使用 scheduleAtFixedRate 方法:

1
2
3
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();

executorService.scheduleAtFixedRate(Classname::someTask, 0, delayInSeconds, TimeUnit.SECONDS);

这将会重复调用 someTask 方法,每次调用之间都会停顿 delayInSeconds

除了支持更多时间相关选项外,ScheduledExecutorService 方法避免了漂移问题,所以可以实现更加准确的时间间隔。

4 总结

这篇文章,我们讨论了 Java 程序中创建延迟的两个方法。


本文为译文,作者通过翻译达到学习目的。 原文链接 | 原文源码链接 | 本站源码链接