从Java ExecutorService捕获线程异常

从Java ExecutorService捕获线程异常,java,multithreading,exception,Java,Multithreading,Exception,我正在研究一个用于并行计算的软件开发框架。我需要一个健壮的机制来报告线程异常。在开发过程中,了解异常来自何处具有很高的价值,因此我希望在过度报告方面犯错误。我还希望能够在线程中处理Junit4测试。下面的方法是合理的还是有更好的方法 import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java

我正在研究一个用于并行计算的软件开发框架。我需要一个健壮的机制来报告线程异常。在开发过程中,了解异常来自何处具有很高的价值,因此我希望在过度报告方面犯错误。我还希望能够在线程中处理Junit4测试。下面的方法是合理的还是有更好的方法

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class TestThreadFailure {

  public static void main(String[] args) {
    int size = 1;
    ExecutorService exec = Executors.newFixedThreadPool(size);
    ThreadFailTask worker = new ThreadFailTask();
    Future<Integer> result = exec.submit(worker);
    try {
      Integer value = result.get();
      System.out.println("Result: " + value);
    } catch (Throwable t) {
      System.out.println("Caught failure: " + t.toString());
      exec.shutdownNow();
      System.out.println("Stack Trace:");
      t.printStackTrace();
      return;
    }
    throw new RuntimeException("Did not catch failure !!");
  }

  public static class ThreadFailTask implements Callable<Integer> {
    @Override
    public Integer call() {
      int nbuf = 65536;
      double[][] buf = new double[nbuf][nbuf];
      return new Integer((int) buf[0][0]);
    }
  }
}
import java.util.concurrent.Callable;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.Executors;
导入java.util.concurrent.Future;
公共类TestThreadFailure{
公共静态void main(字符串[]args){
int size=1;
ExecutorService exec=Executors.newFixedThreadPool(大小);
ThreadFailTask worker=新的ThreadFailTask();
未来结果=执行提交(工人);
试一试{
整数值=result.get();
System.out.println(“结果:+值);
}捕获(可丢弃的t){
System.out.println(“捕获的故障:+t.toString());
exec.shutdownNow();
System.out.println(“堆栈跟踪:”);
t、 printStackTrace();
返回;
}
抛出新的RuntimeException(“未捕获失败!!”);
}
公共静态类ThreadFailTask实现可调用{
@凌驾
公共整数调用(){
int-nbuf=65536;
双精度[][]buf=新双精度[nbuf][nbuf];
返回新整数((int)buf[0][0]);
}
}
}
请考虑呼叫,而不是在网络上。使用
execute()
调用的将在失败时调用

只需创建一个在所有
线程上安装
线程的.UncaughtExceptionHandler
,然后使用
ExecutorService
上的
execute()
调用您的工作,而不是
submit()


看看这一点。

正如本线程中所解释的,只有在实现可运行和不可调用的情况下,使用execute才有效,因为execute无法返回未来


我认为在您的场景中,您应该构建future对象,以便它也可以容纳异常内容。因此,如果出现异常,您将构建错误消息对象。

我认为在使用
submit()
时,没有标准的“钩子”来访问这些异常。但是,如果您需要支持
submit()
(这听起来很合理,因为您使用了
Callable
),那么您可以始终包装Callable和runnable:

ExecutorService executor = new ThreadPoolExecutor(1, 10, 60, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>()) {
    @Override
    public <T> Future<T> submit(final Callable<T> task) {
        Callable<T> wrappedTask = new Callable<T>() {
            @Override
            public T call() throws Exception {
                try {
                    return task.call();
                }
                catch (Exception e) {
                    System.out.println("Oh boy, something broke!");
                    e.printStackTrace();
                    throw e;
                }
            }
        };

        return super.submit(wrappedTask);
    }
};
ExecutorService executor=new ThreadPoolExecutor(1,10,60,TimeUnit.SECONDS,new LinkedBlockingDeque()){
@凌驾
公共未来提交(最终可调用任务){
Callable wrappedTask=新的Callable(){
@凌驾
public T call()引发异常{
试一试{
返回task.call();
}
捕获(例外e){
System.out.println(“哦,天哪,有东西坏了!”);
e、 printStackTrace();
投掷e;
}
}
};
返回super.submit(wrappedTask);
}
};

当然,只有当您首先构建
ExecutorService
时,这种方法才有效。此外,请记住覆盖所有三个
submit()
变体。

我最初的问题是如何使用Java ExecutorService实现“健壮”的线程异常处理。感谢Angelo和Greg为ExecutorService.submit()和Future.get()异常处理提供了指导。我修改后的代码片段如下所示。我在这里学到的关键点是Future.get()捕获所有异常。如果线程被中断或取消,您将获得相应的异常,否则,该异常将被包装并作为ExecutionException重新抛出

import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class TestThreadFailure { public static void main(String[] args) { int size = 1; ExecutorService exec = Executors.newFixedThreadPool(size); ThreadFailTask worker = new ThreadFailTask(); Future result = exec.submit(worker); try { Integer value = result.get(); System.out.println("Result: " + value); } catch (ExecutionException ex) { System.out.println("Caught failure: " + ex.toString()); exec.shutdownNow(); return; } catch (InterruptedException iex) { System.out.println("Thread interrupted: " + iex.toString()); } catch (CancellationException cex) { System.out.println("Thread cancelled: " + cex.toString()); } exec.shutdownNow(); throw new RuntimeException("Did not catch failure !!"); } public static class ThreadFailTask implements Callable { @Override public Integer call() { int nbuf = 65536; double[][] buf = new double[nbuf][nbuf]; return new Integer((int) buf[0][0]); } } } 导入java.util.concurrent.Callable; 导入java.util.concurrent.CancellationException; 导入java.util.concurrent.ExecutionException; 导入java.util.concurrent.ExecutorService; 导入java.util.concurrent.Executors; 导入java.util.concurrent.Future; 公共类TestThreadFailure{ 公共静态void main(字符串[]args){ int size=1; ExecutorService exec=Executors.newFixedThreadPool(大小); ThreadFailTask worker=新的ThreadFailTask(); 未来结果=执行提交(工人); 试一试{ 整数值=result.get(); System.out.println(“结果:+值); }捕获(ExecutionException ex){ System.out.println(“捕获故障:+ex.toString()); exec.shutdownNow(); 返回; }捕获(中断异常iex){ System.out.println(“线程中断:+iex.toString()); }捕获(取消异常cex){ System.out.println(“线程取消:+cex.toString()); } exec.shutdownNow(); 抛出新的RuntimeException(“未捕获失败!!”); } 公共静态类ThreadFailTask实现可调用{ @凌驾 公共整数调用(){ int-nbuf=65536; 双精度[][]buf=新双精度[nbuf][nbuf]; 返回新整数((int)buf[0][0]); } } }
要在ExecutorService中处理异常,您必须利用可调用未来

Callable类似于Runnable,两者都是函数接口,但Runnable的run()不会引发异常,返回类型为void,其中Callable的ascall()返回泛型并引发异常

Java-8方式:

ExecuterService executor = null; 
Future<Integer> future = null;

Callable<Integer> yourTask = () -> {
    //your implementation here();
    //your implementation here();
};

try 
   {
        executor = Executors.newCachedThreadPool();
        future = executor.submit(yourTask );
        Integer result = future.get();
        System.out.println(result);
   }
   catch (ExecutionException | TimeoutException | InterruptedException e) 
   {
    // TODO: handle exception
   }
   finally 
   {
       executer.shutdown();
   }
ExecuterService executor=null;
Future=null;
可调用的yourTask=()->{
//您在这里的实现();
//您在这里的实现();
};
尝试
{
executor=Executors.newCachedThreadPool();
future=执行者。提交(您的任务);
整数结果=future.get();
系统输出打印项次(结果);
}
捕获(ExecutionException | TimeoutException | InterruptedException e)
{
//TODO:处理异常
}
fi
List<Runnable> tasks = new LinkedList<>();
for (int i = 0; i < numThreads; ++i) {
    Runnable task = new Runnable() {
        @Override
        public void run() {
            throw new RuntimeException();
        }
    };

    tasks.add(task);
}

Optional<Throwable> opEmpty = Optional.empty();
/*
 * Use AtomicReference as a means of capturing the first thrown exception, since a
 * spawned thread can't "throw" an exception to the parent thread.
 */
final AtomicReference<Optional<Throwable>> firstThrownException =
        new AtomicReference<>(opEmpty);

/*
 * Use new ThreadPoolExecutor instead of Executors.newFixedThreadPool() so
 * that I can override afterExecute() for the purposes of throwing an
 * exception from the test thread if a child thread fails.
 */
ExecutorService execSvc = new ThreadPoolExecutor(numThreads, numThreads,
            0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()) {
    @Override
    public void afterExecute(Runnable task, Throwable failureCause) {
        if(failureCause == null) {
            // The Runnable completed successfully.
            return;
        }
        // only sets the first exception because it will only be empty on the first call.
        firstThrownException.compareAndSet(Optional.<Throwable>empty(), Optional.of(failureCause));
    }
};

for (Runnable task : tasks) {
    execSvc.execute(task);
}
execSvc.shutdown();
execSvc.awaitTermination(1, TimeUnit.HOURS);

assertEquals(firstThrownException.get(), Optional.empty());