Java 如何正确捕获执行器的运行时异常?
假设我有以下代码:Java 如何正确捕获执行器的运行时异常?,java,concurrency,executor,runtimeexception,Java,Concurrency,Executor,Runtimeexception,假设我有以下代码: ExecutorService executor = Executors.newSingleThreadExecutor(); executor.execute(myRunnable); 现在,如果myRunnable抛出一个runtimeexpetion,我如何捕获它?一种方法是为newSingleThreadExecutor()提供我自己的ThreadFactory实现,并为由此产生的线程设置自定义uncaughtExceptionHandlers。另一种方法是将myR
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
现在,如果
myRunnable
抛出一个runtimeexpetion
,我如何捕获它?一种方法是为newSingleThreadExecutor()
提供我自己的ThreadFactory
实现,并为由此产生的线程设置自定义uncaughtExceptionHandler
s。另一种方法是将myRunnable
包装到包含try-catch-block的本地(匿名)Runnable
。也许还有其他类似的解决办法。但是不知怎么的,这感觉很脏,我觉得不应该这么复杂。有一个干净的解决方案吗?为什么不打电话,回去,然后在打电话时自己处理可能的异常?干净的解决方法是使用ExecutorService.submit()
而不是execute()
。这将返回一个Future
,可用于检索任务的结果或异常:
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
public void run() {
throw new RuntimeException("foo");
}
};
Future<?> future = executor.submit(task);
try {
future.get();
} catch (ExecutionException e) {
Exception rootException = e.getCause();
}
ExecutorService executor=Executors.newSingleThreadExecutor();
Runnable任务=新的Runnable(){
公开募捐{
抛出新的运行时异常(“foo”);
}
};
未来=执行者提交(任务);
试一试{
future.get();
}捕获(执行例外){
异常rootException=e.getCause();
}
skaffman是正确的,因为使用submit
是最干净的方法。另一种方法是子类化并重写afterExecute(Runnable,Throwable)
。如果您采用这种方法,请确保调用execute(Runnable)
而不是submit(Runnable)
或afterExecute
根据API说明:
完成时调用的方法
执行给定的Runnable。这
方法由
执行任务。如果非null,则
可丢弃的是未捕获的
运行时异常
或导致
行刑突然终止
注意:当动作包含在
任务(如FutureTask)或
显式地或通过诸如
提交时,这些任务对象捕获并
维护计算异常,以及
因此,它们不会导致突然的
终止,以及内部
异常未传递给此
方法
在另一个runnable中修饰runnable,该runnable捕获运行时异常并处理它们:
public class REHandler implements Runnable {
Runnable delegate;
public REHandler (Runnable delegate) {
this.delegate = delegate;
}
public void run () {
try {
delegate.run ();
} catch (RuntimeException e) {
... your fancy error handling here ...
}
}
}
executor.execute(new REHandler (myRunnable));
提交给ThreadPoolExecutors
的任务(Callable
或Runnable
)将转换为FuturnTask
,包含一个名为Callable
的属性,该属性等于您提交的任务。FuturnTask有自己的run
方法,如下所示。在c.call()
中抛出的所有异常或可丢弃项将被捕获并放入名为output
的道具中。调用FuturnTask的get
方法时,将抛出output
从Jdk1.8源代码运行futurnstask.run
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// save ex into `outcome` prop
setException(ex);
}
if (ran)
set(result);
}
}
...
}
public void run(){
...
试一试{
可调用c=可调用;
如果(c!=null&&state==NEW){
V结果;
布尔ran;
试一试{
结果=c.call();
ran=真;
}捕获(可丢弃的ex){
结果=空;
ran=假;
//将ex保存到“结果”道具中
setException(ex);
}
如果(运行)
设置(结果);
}
}
...
}
如果要捕获异常,请执行以下操作:
1.斯卡夫曼的回答
2.新建ThreadPoolExecutor时覆盖'afterExecute'
@覆盖
执行后受保护的无效(可运行的r、可丢弃的t){
super.afterExecute(r,t);
可丢弃原因=空;
if(t==null&&r未来实例){
试一试{
((Future)r.get();
}捕获(中断异常|执行异常e){
原因=e;
}
}else如果(t!=null){
原因=t;
}
如果(原因!=null){
//日志错误
}
}
老实说,我质疑捕获在不同线程中抛出的异常的意义。当前线程是否必须加入该线程并等待抛出异常?您在问题中没有提到这一点。@BalusC:将异常从工作线程编组回调用线程是许多应用程序的常见要求。例如,UI应用程序可以调用SwingWorker线程来进行一些后台处理。如果处理失败,则需要将异常传递回事件分派线程。这是一个常见要求。线程1生成一些工作,通过线程2执行,但需要了解它是否成功(即引发异常)。Executor框架可以帮你解决这个问题。嗯,事实上,我还没有想到这一点。我只是好奇,总的来说,如何处理这个问题。但是人们似乎对下面的submit()
和Future
有一些很酷的话要说:-)谢谢,看起来和预期的一模一样。清除。此外,您可能希望使用Callable
而不是Runnable
,然后您的任务可以抛出选中的异常以及未选中的异常。getCause在1.6和1.7中返回一个Throwable而不是异常问题在于future.get()正在阻塞,因此,如果您希望能够异步运行任务,@Loic没有对其进行注释,那么此解决方案没有任何用处,因为它首先破坏了使用执行器的整个目的。
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Throwable cause = null;
if (t == null && r instanceof Future) {
try {
((Future<?>) r).get();
} catch (InterruptedException | ExecutionException e) {
cause = e;
}
} else if (t != null) {
cause = t;
}
if (cause != null) {
// log error
}
}