如何安排任务仅运行一次?

33

我想推迟做某事,类似于设置一个倒计时定时器,在一定时间后“执行某个动作”。

我希望在等待的同时,程序的其余部分能够继续运行,因此我尝试创建一个包含一分钟延迟的自己的 Thread

public class Scratch {
    private static boolean outOfTime = false;

    public static void main(String[] args) {
        Thread countdown = new Thread() {
            @Override
            public void run() {
                try {
                    // wait a while
                    System.out.println("Starting one-minute countdown now...");
                    Thread.sleep(60 * 1000);

                    // do the thing
                    outOfTime = true;
                    System.out.println("Out of time!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        countdown.start();

        while (!outOfTime) {
            try {
                Thread.sleep(1000);
                System.out.println("do other stuff here");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


虽然这种方法勉强可行,但似乎应该有更好的方法。

经过一些搜索,我发现了一些类似的问题,但它们并没有真正解决我想做的事情:

我不需要这么复杂的东西,我只想在一定时间后完成一个单一的任务,而让程序的其余部分继续运行。

我应该如何安排一次性任务来“做一件事”?

2个回答

44

虽然 java.util.Timer 曾经是一种安排未来任务的好方法,但现在更好的做法1是使用 java.util.concurrent 包中的类。

有一个ScheduledExecutorService专门设计用于在延迟后运行命令(或定期执行它们,但这与本问题无关)。

它有一个schedule(Runnable, long, TimeUnit)方法,

创建并执行在给定延迟后启用的一次性操作。


使用 ScheduledExecutorService 您可以像这样重新编写程序:

import java.util.concurrent.*;

public class Scratch {
    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    public static void main(String[] args) {
        System.out.println("Starting one-minute countdown now...");
        ScheduledFuture<?> countdown = scheduler.schedule(new Runnable() {
            @Override
            public void run() {
                // do the thing
                System.out.println("Out of time!");
            }}, 1, TimeUnit.MINUTES);

        while (!countdown.isDone()) {
            try {
                Thread.sleep(1000);
                System.out.println("do other stuff here");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        scheduler.shutdown();
    }
}

通过这种方式做事情的一个好处是,调用schedule()后你可以得到ScheduledFuture<?>对象。这使你可以摆脱额外的boolean变量,并直接检查作业是否已运行。如果你不想再等待,也可以通过调用其cancel()方法来取消预定任务。请参见Java Timer vs ExecutorService?了解避免使用Timer而选择ExecutorService的原因。

这个预定执行器会运行一次还是每分钟都会运行?我希望它只运行一次并退出。 - qualebs
在这个例子中,动作只会运行一次。传递到 schedule() 方法中的时间是动作运行之前的延迟时间。 - azurefrog
@azurefrog 是的,它只运行一次,我希望 countdown 对象从这里提供的 TimeUnit 获取1分钟的值。如何修改此代码以每1分钟连续运行?我的意思是与您现在的代码行为完全相反。 - Suresh
为什么不使用CountDownLatch来替换ScheduleService实现的倒计时对象呢? - qtopierw

0

谢谢,这对我很有用。我使用调度器在运行时计算批处理间隔并安排任务。

    manualTriggerBatchJob.setSchedulingProperties(pblId, batchInterval);
    ScheduledExecutorService scheduledExecutorService =
            Executors.newScheduledThreadPool(5);
    @SuppressWarnings("unchecked")
    ScheduledFuture scheduledFuture =
            scheduledExecutorService.schedule(manualTriggerBatchJob,
            batchIntervalInMin,TimeUnit.MILLISECONDS);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接