如何在Java中实现一定时间后停止执行?

32

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

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

1
似乎我们正在重新讨论一个旧的主题:https://dev59.com/JHE85IYBdhLWcg3w6oB0 - Dmitri
可能是如何超时线程的重复问题。 - vhunsicker
4个回答

54
long start = System.currentTimeMillis();
long end = start + 60*1000; // 60 seconds * 1000 ms/sec
while (System.currentTimeMillis() < end)
{
    // run
}

9
但如果循环的一次迭代是在59秒起始并且每次迭代都需要超过1秒钟,那么你会超过60秒,对吗?这是最好的解决方法吗?我问这个纯属无知。 - Ramy
6
好的,但是检查只会发生在循环开始时。因此,如果在循环开始时已经过去了59秒,并且每次循环迭代需要5秒钟,那么下一次while循环条件被检查时,将会过去64秒钟。对吗,还是我漏掉了什么? - Ramy
6
我相信Ramy是正确的。假设循环中的工作需要7秒钟完成。完成一次后,您需要7秒钟,然后是14秒...直到完成8次迭代,总共需要56秒钟。检查将成功(因为您在60秒以下),但接下来的计算将需要7秒钟。再次进行检查时,您已经用了63秒钟。这超过了时间限制5%。 - MBCook
这个解决方案很好,我的程序没有硬性限制。谢谢大家! - s2000coder
1
@MattBall 为什么中断计算是值得怀疑的?这实际上取决于您的需求。可能您会收到一些反对意见,因为您没有解释您的代码真正的目的->在60秒后防止“运行”代码再次执行。这与“在60秒后终止”完全不同,人们-阅读问题-可能会期望这样做。 - Murmel
显示剩余6条评论

29

你应该尝试一下新的Java Executor Services。 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();
        }
    }
}

这里设置的超时时间是多少? - nikk
“超时”是什么意思?是指 Thread.sleep(1337); 吗? - Murmel
1
我相信@nikk所说的“超时”,是指服务关闭的时间。 - lostsoul29
1
@nikk 超时时间为“1”秒。请查看:f.get() - Sebastian D'Agostino
它不会停止程序。我的程序需要20秒才能完成。它在1秒后显示计算时间太长,但不会停止程序。我仍然可以在20秒后看到输出。 - Rohit Singh
要取消运行,您可以添加:f.cancel(true); - Tyler MacMillan

5
如果您无法超过时间限制(这是一个硬性限制),那么线程是最好的选择。您可以使用循环来在达到时间阈值时终止线程。此时正在进行的任何操作都可以被中断,从而允许计算几乎立即停止。以下是一个示例:
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

这将使你的分辨率达到约半秒。通过在while循环中更频繁地轮询,您可以将其降低。

你的可运行代码应该像这样:

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,它可以让你避免循环。你只需做join调用,设置好的TimerTask会负责中断线程。这样可以获得更精确的分辨率,而无需在循环中轮询。


这不是 .interrupt() 的工作方式,除非线程被阻塞,否则它只会设置 interrupted 标志,线程仍然必须检查它以停止执行。此外,InterruptedException 是一个已检查的异常,你不能在任意代码中随意抛出它(你的示例将无法编译)。 - Dmitri

2

取决于while循环正在执行的任务。如果存在长时间阻塞的可能,请使用TimerTask调度任务来设置stopExecution标志,并且.interrupt()您的线程。

仅使用时间条件的循环可能会一直等待输入或锁定(但对您来说可能不是问题)。


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