15

我有一个我想调用的方法。但是,如果执行时间过长,我正在寻找一种干净、简单的方法来杀死它或强制它返回。

我正在使用 Java。

为了显示:

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

我认为这TestExecutor门课应该implement Callable并继续朝着这个方向前进。

但我想做的就是executor.execute()在时间太长时停下来。

建议...?

编辑

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

4

7 回答 7

11

你应该看看这些类: FutureTaskCallableExecutors

这是一个例子:

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<Object> callable = new Callable<Object>() {
            public Object call() throws Exception {
                return myMethod();
            }
        };
        ExecutorService executorService = Executors.newCachedThreadPool();

        Future<Object> 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");
        }
    }
}
于 2008-10-27T22:03:36.033 回答
8

Java 的中断机制就是为这种场景设计的。如果您希望中止的方法正在执行一个循环,只需让它在每次迭代时检查线程的中断状态。如果它被中断,则抛出一个 InterruptedException。

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

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

于 2008-10-27T15:56:54.137 回答
7

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

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

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

于 2008-10-27T15:40:03.187 回答
3

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

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

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

对于现实生活中这种方法的一个示例,Eclipse RCP 的 Job API 的“IPProgressMonitor”对象允许某些管理服务(通过“取消”方法)向子流程发出信号,它们应该停止。当然,这依赖于定期实际检查 isCancelled 方法的方法,而他们经常无法做到这一点。

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

为了测试这种方法,我编写了一个简单的工具,它接受一个可运行文件并尝试执行它。随意评论/编辑。

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 对象以查看用户是否希望系统提供向上。将来我会尝试遵循这种模式,尤其是可能是交互式的方法。当然,您不一定事先知道将以这种方式使用哪些方法,但我猜这就是 Profiler 的用途。

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

于 2008-10-27T15:54:53.610 回答
1

没有人直接回答它,所以这是我可以用少量伪代码给你的最接近的东西:

将方法包装在可运行/可调用中。如果要停止该方法本身,则必须检查中断状态(例如,如果此方法是循环,则在循环内部检查 Thread.currentThread().isInterrupted ,如果是,则停止循环(不要'不要检查每次迭代,否则你只会减慢速度。在包装方法中,使用 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 否则该线程将在您调用中断后继续运行。

于 2008-10-27T17:16:00.437 回答
0

我相信正确的答案是创建一个 Runnable 来执行子程序,并在单独的线程中运行它。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
于 2013-04-19T03:58:56.000 回答
0

我能想到一个不太好的方法来做到这一点。如果您可以检测到何时花费了太多时间,则可以让该方法在每个步骤中检查布尔值。如果花费太多时间,让程序将 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
  }
于 2014-05-06T11:41:25.847 回答