【问题标题】:How to stop execution after a certain time in Java?如何在Java中经过一段时间后停止执行?
【发布时间】:2023-06-19 11:15:02
【问题描述】:

在代码中,变量 timer 将指定结束 while 循环的持续时间,例如 60 秒。

   while(timer) {
    //run
    //terminate after 60 sec
   }

【问题讨论】:

标签: java time


【解决方案1】:
long start = System.currentTimeMillis();
long end = start + 60*1000; // 60 seconds * 1000 ms/sec
while (System.currentTimeMillis() < end)
{
    // run
}

【讨论】:

  • 但是如果循环的一次迭代在 59 秒开始时开始并且循环的每次迭代花费超过 1 秒,那么您会超过 60 秒,对吗?这是可以做到的最好的吗。我只是出于无知而问。
  • @Ramy:不。 startend 是绝对时间,以自纪元(UTC 1970 年 1 月 1 日午夜)以来的毫秒数为单位。没有翻转。
  • 好的,但是检查只发生在循环的开始。因此,如果在循环开始时,例如 59 秒已经过去,并且循环的每次迭代需要 5 秒。下次检查 while 循环条件时,将经过 64 秒。对,还是我错过了什么?
  • 我相信拉米是正确的。假设循环中的工作需要 7 秒才能完成。一次运行后,你在 7 秒,然后是 14 秒……直到你完成 8 次迭代,总共 56 秒。检查将成功(因为您未满 60 岁),但随后计算将需要 7 秒。再次完成检查时,您的时间为 63 秒。随着时间的推移,这是 5%。
  • @MattBall 为什么中断计算值得怀疑?这真的取决于你的需求。可能你得到了一些反对意见,因为你没有解释你的代码真正想要做什么 -> 防止“运行”代码在 60 秒后再次执行。这与“60 秒后终止”完全不同,阅读问题的人们可能会这样做。
【解决方案2】:

您应该尝试新的 Java 执行器服务。 http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html

有了这个,你就不需要自己编程循环来测量时间了。

public class Starter {

    public static void main(final String[] args) {
        final ExecutorService service = Executors.newSingleThreadExecutor();

        try {
            final Future<Object> f = service.submit(() -> {
                // Do you long running calculation here
                Thread.sleep(1337); // Simulate some delay
                return "42";
            });

            System.out.println(f.get(1, TimeUnit.SECONDS));
        } catch (final TimeoutException e) {
            System.err.println("Calculation took to long");
        } catch (final Exception e) {
            throw new RuntimeException(e);
        } finally {
            service.shutdown();
        }
    }
}

【讨论】:

  • 这里设置的超时值是多少?
  • “超时”是什么意思? Thread.sleep(1337);?
  • 我相信“超时”,@nikk 是指服务将关闭的时间量。
  • @nikk 超时为“1”秒。看:f.get()
  • 它不会停止程序。我的程序需要 20 秒才能完成。它确实显示计算在 1 秒后花费了很长时间,但它并没有停止程序。 20 秒后我仍然看到输出。
【解决方案3】:

如果你不能超过你的时间限制(这是一个硬限制),那么线程是你最好的选择。达到时间阈值后,您可以使用循环终止线程。当时该线程中发生的任何事情都可以被中断,从而使计算几乎立即停止。这是一个例子:

Thread t = new Thread(myRunnable); // myRunnable does your calculations

long startTime = System.currentTimeMillis();
long endTime = startTime + 60000L;

t.start(); // Kick off calculations

while (System.currentTimeMillis() < endTime) {
    // Still within time theshold, wait a little longer
    try {
         Thread.sleep(500L);  // Sleep 1/2 second
    } catch (InterruptedException e) {
         // Someone woke us up during sleep, that's OK
    }
}

t.interrupt();  // Tell the thread to stop
t.join();       // Wait for the thread to cleanup and finish

这将使您的分辨率约为 1/2 秒。通过在 while 循环中更频繁地进行轮询,您可以将其降低。

你的 runnable 的运行看起来像这样:

public void run() {
    while (true) {
        try {
            // Long running work
            calculateMassOfUniverse();
        } catch (InterruptedException e) {
            // We were signaled, clean things up
            cleanupStuff();
            break;           // Leave the loop, thread will exit
    }
}

根据 Dmitri 的回答更新

Dmitri 指出TimerTask,它可以让你避免循环。您可以只进行加入调用,而您设置的 TimerTask 将负责中断线程。这将使您无需循环轮询即可获得更精确的分辨率。

【讨论】:

  • 这不是.interrupt() 的工作原理。除非线程被阻塞,它只是设置interrupted 标志,线程仍然必须检查它以停止执行。另外,InterruptedException 是一个检查异常,你不能随便乱扔一些任意代码(你的例子不会编译)。
【解决方案4】:

取决于 while 循环在做什么。如果有可能会阻塞很长时间,请使用TimerTask 安排任务来设置stopExecution 标志,以及.interrupt() 你的线程。

在循环中只有一个时间条件,它可以永远坐在那里等待输入或锁定(再说一遍,对你来说可能不是问题)。

【讨论】: