如何包装方法,以便在超过指定的超时时可以终止执行?

我有一个方法,我想打电话。 但是,我正在寻找一种干净,简单的方法来杀死它或强迫它返回,如果执行时间太长。

我正在使用Java。

为了显示:

logger.info("sequentially executing all batches..."); for (TestExecutor executor : builder.getExecutors()) { logger.info("executing batch..."); executor.execute(); } 

我认为TestExecutor类应该implement Callable并继续朝这个方向发展。

但我想要做的就是停止executor.execute()如果它花了太长时间。

建议…?

编辑

收到的许多建议都假设正在执行的方法需要很长时间才能包含某种循环,并且可以定期检查变量。 然而,这种情况并非如此。 因此,某些东西不一定是干净的,只会停止执行,这是可以接受的。

您应该看一下这些类: FutureTask , Callable , Executors

这是一个例子:

 public class TimeoutExample { public static Object myMethod() { // does your thing and taking a long time to execute return someResult; } public static void main(final String[] args) { Callable callable = new Callable() { public Object call() throws Exception { return myMethod(); } }; ExecutorService executorService = Executors.newCachedThreadPool(); Future task = executorService.submit(callable); try { // ok, wait for 30 seconds max Object result = task.get(30, TimeUnit.SECONDS); System.out.println("Finished with result: " + result); } catch (ExecutionException e) { throw new RuntimeException(e); } catch (TimeoutException e) { System.out.println("timeout..."); } catch (InterruptedException e) { System.out.println("interrupted"); } } } 

Java的中断机制适用于这种情况。 如果您希望中止的方法是执行循环,只需让它在每次迭代时检查线程的中断状态 。 如果它被中断,则抛出InterruptedException。

然后,当你想要中止时,你只需要在适当的线程上调用中断 。

或者,您可以使用Sun建议的方法替代已弃用的停止方法。 这不涉及抛出任何exception,该方法只会正常返回。

我假设在以下语句中使用多个线程。

我已经在这个领域做了一些阅读,大多数作者说杀死另一个线程是个坏主意。

如果要杀死的函数可以设计为定期检查变量或同步原语,然后在设置该变量或同步原语时干净地终止,那将非常干净。 然后某种监视器线程可以hibernate几毫秒,然后设置变量或同步原语。

真的,你不能……唯一的方法是使用thread.stop,同意一个’合作’方法(例如,偶尔检查Thread.isInterrupted或调用抛出InterruptedException的方法,例如Thread。 sleep()),或以某种方式完全调用另一个JVM中的方法。

对于某些类型的测试,调用stop()是可以的,但它可能会损坏测试套件的状态,所以如果你想避免交互影响,你必须在每次调用stop()之后重新启动JVM。

有关如何实现协作方法的详细说明,请查看Sun关于弃用的Thread方法的常见问题解答 。

对于现实生活中这种方法的一个例子, Eclipse RCP的Job API的 ‘IProgressMonitor’对象允许一些管理服务通知它们应该停止的子进程(通过’cancel’方法)。 当然,这依赖于定期实际检查isCancelled方法的方法,而这些方法通常无法做到。

混合方法可能是通过中断很好地询问线程,然后在几秒钟之后坚持停止。 同样,你不应该在生产代码中使用stop,但在这种情况下它可能没问题,尤其是。 如果你很快退出JVM。

为了测试这种方法,我编写了一个简单的线束,它使用runnable并尝试执行它。 随意评论/编辑。

 public void testStop(Runnable r) { Thread t = new Thread(r); t.start(); try { t.join(2000); } catch (InterruptedException e) { throw new RuntimeException(e); } if (!t.isAlive()) { System.err.println("Finished on time."); return; } try { t.interrupt(); t.join(2000); if (!t.isAlive()) { System.err.println("cooperative stop"); return; } } catch (InterruptedException e) { throw new RuntimeException(e); } System.err.println("non-cooperative stop"); StackTraceElement[] trace = Thread.getAllStackTraces().get(t); if (null != trace) { Throwable temp = new Throwable(); temp.setStackTrace(trace); temp.printStackTrace(); } t.stop(); System.err.println("stopped non-cooperative thread"); } 

为了测试它,我写了两个竞争的无限循环,一个合作,一个永远不会检查其线程的中断位。

 public void cooperative() { try { for (;;) { Thread.sleep(500); } } catch (InterruptedException e) { System.err.println("cooperative() interrupted"); } finally { System.err.println("cooperative() finally"); } } public void noncooperative() { try { for (;;) { Thread.yield(); } } finally { System.err.println("noncooperative() finally"); } } 

最后,我编写了测试(JUnit 4)来练习它们:

 @Test public void testStopCooperative() { testStop(new Runnable() { @Override public void run() { cooperative(); } }); } @Test public void testStopNoncooperative() { testStop(new Runnable() { @Override public void run() { noncooperative(); } }); } 

我之前从未使用过Thread.stop(),所以我没有意识到它的操作。 它的工作原理是从目标线程当前运行的东西中抛出一个ThreadDeath对象。 这扩展了错误。 因此,虽然它并不总是干净利落,但它通常会使程序状态相当合理的简单程序。 例如,调用任何finally块。 如果你想成为一个真正的混蛋,你可以抓住 ThreadDeath(或错误),并继续运行!

如果没有别的,这真的让我希望更多的代码遵循IProgressMonitor方法 – 向可能需要一段时间的方法添加另一个参数,并鼓励方法的实现者偶尔轮询Monitor对象以查看用户是否希望系统给出向上。 我将来会尝试遵循这种模式,特别是可能是交互式的方法。 当然,你不一定事先知道哪种方法会以这种方式使用,但我认为这就是Profilers的用途。

至于’开始另一个JVM完全’的方法,这将需要更多的工作。 我不知道是否有人编写了委托类加载器,或者是否包含在JVM中,但是这种方法需要这样做。

没有人直接回答,所以这是我能用短暂的伪代码给你的最接近的东西:

将方法包装在runnable / callable中。 如果你想让它停止,方法本身就必须检查中断状态(例如,如果这个方法是循环,在循环内检查Thread.currentThread()。isInterrupted,如果是,则停止循环(don但是,检查每次迭代,或者你只是放慢速度。在包装方法中,使用thread.join(timeout)等待你想让方法运行的时间。或者在循环内部,调用join如果你需要在等待时做其他事情,则反复使用较小的超时。如果方法没有完成,在加入后,使用上述建议中止快速/清除。

所以代码明智,旧代码:

 void myMethod() { methodTakingAllTheTime(); } 

新代码:

 void myMethod() { Thread t = new Thread(new Runnable() { public void run() { methodTakingAllTheTime(); // modify the internals of this method to check for interruption } }); t.join(5000); // 5 seconds t.interrupt(); } 

但同样,为了使其运行良好,您仍然需要修改methodTakingAllTheTime,否则该线程将在您调用中断后继续运行。

我相信,正确的答案是创建一个Runnable来执行子程序,并在一个单独的Thread中运行它。 Runnable可能是FutureTask,你可以使用超时(“get”方法)运行。 如果它超时,你会得到一个TimeoutException,我建议你

  • 调用thread.interrupt()尝试以半协作的方式结束它(许多库调用似乎对此敏感,所以它可能会工作)
  • 稍等一下(Thread.sleep(300))
  • 然后,如果线程仍处于活动状态(thread.isActive()),则调用thread.stop()。 这是一种不赞成使用的方法,但显然是城里唯一一款不会运行单独进程的游戏。

在我的应用程序中,我运行由初学者编写的不可信,不合作的代码,我执行上述操作,确保被杀死的线程永远不会(写)访问任何在其死亡后幸存的对象。 这包括容纳被调用方法的对象,如果发生超时则将其丢弃。 (我告诉我的学生避免超时,因为他们的经纪人将被取消资格。)我不确定内存泄漏…

我区分长运行时(方法终止)和硬超时 – 硬超时更长,并且意味着在代码根本不终止时捕获这种情况,而不是缓慢。

根据我的研究,Java似乎没有一个非弃用的运行非合作代码的规定,这在某种程度上是安全模型中的一个漏洞。 要么我可以运行外部代码并控制它拥有的权限(SecurityManager),要么我无法运行外部代码,因为它可能最终占用整个CPU而没有不弃用的方法来阻止它。

 double x = 2.0; while(true) {x = x*x}; // do not terminate System.out.print(x); // prevent optimization 

我可以想到一个不那么好的方法来做到这一点。 如果您可以检测何时花费太多时间,则可以让方法在每个步骤中检查布尔值。 如果程序占用太多时间,程序会将boolean tooMuchTime的值更改为true(我无法帮助解决此问题)。 然后使用这样的东西:

  Method(){ //task1 if (tooMuchTime == true) return; //task2 if (tooMuchTime == true) return; //task3 if (tooMuchTime == true) return; //task4 if (tooMuchTime == true) return; //task5 if (tooMuchTime == true) return; //final task }