您可以使用自定义的FutureTask来帮助自己:
public class TracingFutureTask extends FutureTask {
private Throwable trace;
private boolean done;
public TracingFutureTask(Callable callable) {
super(callable);
}
public TracingFutureTask(Runnable runnable, T result) {
super(runnable, result);
}
@Override
public void run() {
try { super.run(); }
finally { synchronized(this) { done=true; notifyAll(); }}
}
@Override
protected void setException(Throwable t) {
trace=t;
super.setException(t);
}
public synchronized Throwable getException() throws InterruptedException {
while(!done) wait();
return trace;
}
public synchronized Throwable getException(long timeout)
throws InterruptedException, TimeoutException {
for(long deadline = System.currentTimeMillis()+timeout, toWait=timeOut;
!done; toWait = deadline-System.currentTimeMillis()) {
if ( toWait <=0 ) throw new TimeoutException(
"Thread did not end in " + timeout + " milliseconds!" );
wait(toWait);
}
return trace;
}
public static TracingFutureTask submit(Executor e, Callable c) {
TracingFutureTask ft=new TracingFutureTask<>(c);
e.execute(ft);
return ft;
}
public static TracingFutureTask submit(Executor e, Runnable r, V v) {
TracingFutureTask ft=new TracingFutureTask<>(r, v);
e.execute(ft);
return ft;
}
}
这会跟踪基类的异常,但与基类不同,它甚至在作业被取消时也会记住它.这就是为什么run()方法和getException()之间存在额外的同步,就像在取消情况下作业可以在记录异常之前进入取消状态(这意味着“完成”),因此我们必须介绍我们自己的完成状态与适当的同步.
它可以像:
ExecutorService executor = Executors.newSingleThreadExecutor();
TracingFutureTask future=TracingFutureTask.submit(executor, new Callable(){
@Override
public synchronized String call() throws Exception {
this.wait( 60000 );
return "foo";
}
});
try {
future.get(500, TimeUnit.MILLISECONDS);
fail("Timeout expected.");
} catch (ExecutionException | TimeoutException e) {
e.printStackTrace();
}
if(future.cancel(true)) {
System.err.println("cancelled.");
Throwable t = future.getException();
if(t!=null) t.printStackTrace(System.err.append("cancellation caused "));
}
(从您的示例代码派生)
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask.get(FutureTask.java:205)
at so.TestCancel.main(TestCancel.java:69)
cancelled.
cancellation caused java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at so.TestCancel$1.call(TestCancel.java:64)
at so.TestCancel$1.call(TestCancel.java:61)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at so.TracingFutureTask.run(TestCancel.java:33)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)