我试图使用Java的ThreadPoolExecutor类运行大量具有固定数量线程的重量级任务。每个任务都有许多可能由于异常而失败的地方。
我已经继承了ThreadPoolExecutor的子类,并且重写了afterExecute方法,该方法应该提供在运行任务时遇到的任何未捕获的异常。然而,我似乎不能让它工作。
例如:
public class ThreadPoolErrors extends ThreadPoolExecutor {
public ThreadPoolErrors() {
super( 1, // core threads
1, // max threads
1, // timeout
TimeUnit.MINUTES, // timeout units
new LinkedBlockingQueue<Runnable>() // work queue
);
}
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
System.out.println("Got an error: " + t);
} else {
System.out.println("Everything's fine--situation normal!");
}
}
public static void main( String [] args) {
ThreadPoolErrors threadPool = new ThreadPoolErrors();
threadPool.submit(
new Runnable() {
public void run() {
throw new RuntimeException("Ouch! Got an error.");
}
}
);
threadPool.shutdown();
}
}
这个程序的输出是“一切正常——情况正常!”,尽管提交给线程池的唯一Runnable抛出了异常。你知道这里发生了什么吗?
谢谢!
警告:需要注意的是,此解决方案将阻塞将来的调用线程。get()。
如果您想处理任务抛出的异常,那么通常使用Callable而不是Runnable更好。
Callable.call()允许抛出检查过的异常,这些异常会传播回调用线程:
Callable task = ...
Future future = executor.submit(task);
// do something else in the meantime, and then...
try {
future.get();
} catch (ExecutionException ex) {
ex.getCause().printStackTrace();
}
如果Callable.call()抛出异常,则该异常将被包装在ExecutionException中并由Future.get()抛出。
这可能比继承ThreadPoolExecutor的子类要好得多。如果异常是可恢复的,它还为您提供了重新提交任务的机会。
如果你的ExecutorService来自外部源(即不可能子类化ThreadPoolExecutor并覆盖afterExecute()),你可以使用动态代理来实现所需的行为:
public static ExecutorService errorAware(final ExecutorService executor) {
return (ExecutorService) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[] {ExecutorService.class},
(proxy, method, args) -> {
if (method.getName().equals("submit")) {
final Object arg0 = args[0];
if (arg0 instanceof Runnable) {
args[0] = new Runnable() {
@Override
public void run() {
final Runnable task = (Runnable) arg0;
try {
task.run();
if (task instanceof Future<?>) {
final Future<?> future = (Future<?>) task;
if (future.isDone()) {
try {
future.get();
} catch (final CancellationException ce) {
// Your error-handling code here
ce.printStackTrace();
} catch (final ExecutionException ee) {
// Your error-handling code here
ee.getCause().printStackTrace();
} catch (final InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
} catch (final RuntimeException re) {
// Your error-handling code here
re.printStackTrace();
throw re;
} catch (final Error e) {
// Your error-handling code here
e.printStackTrace();
throw e;
}
}
};
} else if (arg0 instanceof Callable<?>) {
args[0] = new Callable<Object>() {
@Override
public Object call() throws Exception {
final Callable<?> task = (Callable<?>) arg0;
try {
return task.call();
} catch (final Exception e) {
// Your error-handling code here
e.printStackTrace();
throw e;
} catch (final Error e) {
// Your error-handling code here
e.printStackTrace();
throw e;
}
}
};
}
}
return method.invoke(executor, args);
});
}
医生的例子并没有给我想要的结果。
当线程进程被放弃(带有显式的interput();s)时,出现异常。
我也想保留这个“系统”。exit”的功能,一个正常的主线程有一个典型的抛出,我想这样程序员就不会被迫在代码上工作,而不得不担心它的上下文(…一个线程),如果出现任何错误,它必须是一个编程错误,或者这种情况必须解决的地方,手动捕获…没有必要过于复杂。
所以我修改了代码来满足我的需要。
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future<?>) {
Future<?> future = (Future<?>) r;
boolean terminate = false;
try {
future.get();
} catch (ExecutionException e) {
terminate = true;
e.printStackTrace();
} catch (InterruptedException | CancellationException ie) {// ignore/reset
Thread.currentThread().interrupt();
} finally {
if (terminate) System.exit(0);
}
}
}
但是要小心,这段代码基本上将你的线程转换为主线程异常,同时保持所有的并行属性…但是让我们面对现实,在系统的并行机制(扩展Thread)的功能上设计架构是错误的方法……除非严格要求使用事件驱动设计....但后来…如果这是需求,那么问题是:在这种情况下是否需要ExecutorService ?……也许不是。