簡體   English   中英

從 Java ExecutorService 捕獲線程異常

[英]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.UncaughtExceptionHandlerThreadFactory ,然后在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 中處理異常,您必須利用CallableFuture

CallableRunnable類似,兩者都是函數式接口,但 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM