如何让 FutureTask 在 TimeoutException 后返回?

7 浏览
0 Comments

如何让 FutureTask 在 TimeoutException 后返回?

在下面的代码中,我按预期在100秒后捕获了TimeoutException。此时,我期望代码从主函数中退出并终止程序,但是它继续向控制台打印。如何在超时后停止执行任务?

private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
private static  T timedCall(Callable c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
    FutureTask task = new FutureTask(c);
    THREAD_POOL.execute(task);
    return task.get(timeout, timeUnit);
}
public static void main(String[] args) {
    try {
        int returnCode = timedCall(new Callable() {
            public Integer call() throws Exception {
                for (int i=0; i < 1000000; i++) {
                    System.out.println(new java.util.Date());
                    Thread.sleep(1000);
                }
                return 0;
            }
        }, 100, TimeUnit.SECONDS);
    } catch (Exception e) {
        e.printStackTrace();
        return;
    }
}

0
0 Comments

问题出现的原因是:在使用FutureTask的cancel方法时,需要在Callable的代码中处理线程被中断的情况。而在给定的代码中,通过调用Thread.sleep()方法来处理线程被中断的情况,因此可以正常地调用futureTask.cancel()方法。

解决方法是:在异常处理程序中调用futureTask.cancel(true)方法来取消和中断运行任务的线程。建议学习有关中断机制的知识,并在代码中进行合适的处理。

以下为整理后的文章:

你的Callable必须能够在需要时快速停止。

你的代码:

public Integer call() throws Exception {
    for (int i=0; i < 1000000 && !task.cancelled(); i++) {
        System.out.println(new java.util.Date());
        Thread.sleep(1000); // 当线程被中断时抛出InterruptedException
    }
    return 0;
}

通过调用Thread.sleep()方法,你的代码已经能够在线程被中断时做出相应的反应。问题在于futureTask.cancel(true)会中断其他线程,并且你的代码需要对此中断做出反应。Thread.sleep()就是用来处理这个中断的。如果你没有使用Thread.sleep()或其他可中断的阻塞代码,你将需要自己检查Thread.currentThread().isInterrupted(),并在发现为true时尽快退出(例如通过抛出new InterruptedException())。

你需要在异常处理程序中调用futureTask.cancel(true)来取消和中断运行任务的线程。

我的建议是学习中断机制(这是一篇很好的文章:处理InterruptedException),并加以利用。

0
0 Comments

问题的出现原因:

当使用FutureTask等待任务完成时,如果任务执行超时,会抛出TimeoutException异常。此时,需要根据实际需求来处理这个异常。以下是一些处理TimeoutException的方法。

解决方法:

1. 调用任务的cancel(true)方法:通过调用任务的cancel方法,并传入true参数,可以取消任务的执行。这将导致任务被中断,并且FutureTask的get方法将抛出CancellationException异常。

futureTask.cancel(true);

2. 关闭ExecutorService:如果你使用了ExecutorService来执行任务,你可以调用shutdownNow方法来关闭ExecutorService,这将导致所有正在执行的任务被中断。

executorService.shutdownNow();

3. 终止JVM:如果你想在超时发生时终止整个Java虚拟机,你可以调用System.exit(0)方法。这将立即终止整个应用程序。

System.exit(0);

根据你的实际需求,选择适合的处理方法来处理TimeoutException异常。

0
0 Comments

你需要在超时后取消任务(并中断它的线程)。这就是cancel(true)方法的作用。

private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();
private static  T timedCall(FutureTask task, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
    THREAD_POOL.execute(task);
    return task.get(timeout, timeUnit);
}
public static void main(String[] args) {
    try {
        FutureTask task = new FutureTask(new Callable() {
            public Integer call() throws Exception {
                for (int i=0; i < 1000000; i++) {
                    if (Thread.interrupted()) return 1;
                    System.out.println(new java.util.Date());
                    Thread.sleep(1000);
                }
                return 0;
            }
        });
        int returnCode = timedCall(task, 100, TimeUnit.SECONDS);
    } catch (Exception e) {
        e.printStackTrace();
        task.cancel(true);
    }
    return;
}

!task.cancelled()应该改为!isCancelled(),因为djna最初的写法是这样的。

我认为最好使用中断。首先,你的可调用代码根本不需要知道task。其次,当你在可调用代码中使用各种阻塞操作(如Thread.sleep())时,它们不会对task.isCancelled()做出反应,但通常会对中断做出反应。因此,使用cancel(true)并让你的代码意识到中断通常是最好的方法。(你的代码也会更通用,因为中断机制在Java中被广泛使用)

我想我要说的是“中断是一种协作机制。”(ibm.com/developerworks/java/library/j-jtp05236.html

不错的文章!我已经根据此更改了答案。

清晰的代码未经测试。catch块中的task将未定义。

0