![](/img/trans.png)
[英]Catching Exceptions from (ExecutorService) CachedThreadPool
[英]Catching thread exceptions from Java ExecutorService
我正在開發用於並行計算的軟件開發框架JavaSeis.org 。 我需要一個強大的機制來報告線程異常。 在開發過程中,知道異常來自哪里具有很高的價值,所以我想在過度報告方面犯錯。 我還希望能夠在線程中處理 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]);
}
}
}
考慮在ExecutorService
上調用execute()
而不是submit()
。 使用execute()
調用的Thread
將在Thread.UncaughtExceptionHandler
時調用Thread.UncaughtExceptionHandler
。
只需創建一個在所有Threads
上安裝Thread.UncaughtExceptionHandler
的ThreadFactory
,然后在ExecutorService
上使用execute()
而不是submit()
調用您的工作。
看看這個相關的堆棧溢出問題。
我不相信在使用submit()
時有一個標准的“鈎子”可以解決這些異常。 但是,如果您需要支持submit()
(這聽起來很合理,因為您使用的是Callable
),您始終可以包裝 Callables 和 Runnables :
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
時才有效。 此外,請記住覆蓋所有三個submit()
變體。
正如該線程中所解釋的,使用ThreadPoolExecutor 提交和執行方法有什么區別,只有在實現 Runnable 而不是 Callable 時使用 execute 才有效,因為 execute 不能返回 Future。
我認為在您的場景中,您應該構建未來對象,以便它也可以容納異常內容。 因此,如果出現異常,您將構建錯誤消息對象。
我最初的問題是問如何使用 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]); } } }
我對其他答案的運氣並不好,因為我需要實際的異常實例本身,而不僅僅是打印的堆棧跟蹤。 對我來說,關於問題“ 為什么 UncaughtExceptionHandler 沒有被 ExecutorService 調用? ”的ThreadPoolExecutor#afterExecute()的公認答案有效。
請參閱以下示例代碼:
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());
要在ExecutorService 中處理異常,您必須利用Callable和Future 。
Callable與Runnable類似,兩者都是函數式接口,但 Runnable 的run()不會拋出異常,並且返回類型為 void,而 Callable 的call()返回泛型並拋出異常。
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();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.