如何让 FutureTask 在 TimeoutException 后返回?
如何让 FutureTask 在 TimeoutException 后返回?
在下面的代码中,我按预期在100秒后捕获了TimeoutException。此时,我期望代码从主函数中退出并终止程序,但是它继续向控制台打印。如何在超时后停止执行任务?
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool(); private staticT 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; } }
问题出现的原因是:在使用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),并加以利用。
问题的出现原因:
当使用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异常。
你需要在超时后取消任务(并中断它的线程)。这就是cancel(true)
方法的作用。
private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool(); private staticT 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
将未定义。