Java中连续调用方法时如何实现异步延迟执行?
问题:调用延迟方法后任务同时执行,需实现间隔1分钟依次输出
我想让每次调用printFirst()方法前延迟1分钟,且不影响主线程运行。用Timer写了如下代码:
// define delaying print-method using Timer static void printFirst() { new java.util.Timer().schedule( new java.util.TimerTask() { public void run() { System.out.println(ts() + " First"); } },60000 ); } // main to run System.out.println(ts() + " Zero"); printFirst(); printFirst(); printFirst(); System.out.println(ts() + " Second"); System.out.println(ts() + " Third"); System.out.println(ts() + " Fourth");
实际输出中三条含“First”的日志同时打印:
Timestamp: 2023-01-05 17:40:43.664 Zero Timestamp: 2023-01-05 17:40:43.666 Second Timestamp: 2023-01-05 17:40:43.667 Third Timestamp: 2023-01-05 17:40:43.667 Fourth Timestamp: 2023-01-05 17:41:13.681 First Timestamp: 2023-01-05 17:41:13.681 First Timestamp: 2023-01-05 17:41:13.681 First
期望三条日志之间间隔1分钟输出:
Timestamp: 2023-01-05 17:40:43.664 Zero Timestamp: 2023-01-05 17:40:43.666 Second Timestamp: 2023-01-05 17:40:43.667 Third Timestamp: 2023-01-05 17:40:43.667 Fourth Timestamp: 2023-01-05 17:41:43.667 First Timestamp: 2023-01-05 17:42:43.667 First Timestamp: 2023-01-05 17:43:43.667 First
问题原因
你每次调用printFirst()都会新建一个java.util.Timer实例,每个Timer独立调度自己的任务,且都设置了60秒的延迟。三次调用几乎在同一瞬间完成,所以三个任务的触发时间完全一致,导致日志同时输出。
解决方案
方案1:复用单个Timer,累加延迟时间
维护一个全局的Timer实例和延迟计数器,每次调用时让延迟时间累加1分钟,确保任务依次推后执行:
import java.util.Timer; import java.util.TimerTask; public class DelayedPrint { private static final Timer timer = new Timer(); private static long accumulatedDelay = 0; private static final long ONE_MINUTE = 60 * 1000; static void printFirst() { accumulatedDelay += ONE_MINUTE; timer.schedule(new TimerTask() { @Override public void run() { System.out.println(ts() + " First"); } }, accumulatedDelay); } private static String ts() { return java.time.LocalDateTime.now() .format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); } public static void main(String[] args) { System.out.println(ts() + " Zero"); printFirst(); printFirst(); printFirst(); System.out.println(ts() + " Second"); System.out.println(ts() + " Third"); System.out.println(ts() + " Fourth"); } }
方案2:使用ScheduledExecutorService(推荐)
java.util.Timer存在单线程执行、任务异常会导致整个Timer停止的缺陷,更推荐用Java并发包的ScheduledExecutorService,它是更健壮的调度工具:
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class DelayedPrint { private static final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private static long accumulatedDelay = 0; private static final long ONE_MINUTE = 1; static void printFirst() { accumulatedDelay += ONE_MINUTE; scheduler.schedule(() -> { System.out.println(ts() + " First"); }, accumulatedDelay, TimeUnit.MINUTES); } private static String ts() { return java.time.LocalDateTime.now() .format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")); } public static void main(String[] args) { System.out.println(ts() + " Zero"); printFirst(); printFirst(); printFirst(); System.out.println(ts() + " Second"); System.out.println(ts() + " Third"); System.out.println(ts() + " Fourth"); // 程序正常结束时关闭调度器,避免线程残留 // scheduler.shutdown(); } }
两种方案都能保证主线程不被阻塞,同时实现三条First日志依次间隔1分钟输出,方案2更适合生产环境使用。
内容的提问来源于stack exchange,提问作者Sangmin Kim
相关产品推荐
相关产品推荐

