2009-11-06 12 views
43

powiedzieć, że mam następujący kod:Jak poprawnie uchwycić wyjątki RuntimeException z Executorów?

ExecutorService executor = Executors.newSingleThreadExecutor(); 
executor.execute(myRunnable); 

Teraz, jeśli myRunnable rzuca RuntimeExcpetion, w jaki sposób można go złapać? Jednym ze sposobów jest dostarczenie mojej własnej implementacji ThreadFactory do newSingleThreadExecutor() i ustawienie niestandardowych uncaughtExceptionHandler s dla Thread s, które z niej wyjdą. Innym sposobem byłoby zawinięcie myRunnable do lokalnego (anonimowego) Runnable zawierającego blokadę try-catch. Być może są też inne podobne rozwiązania. Ale ... jakoś to wydaje się brudne, uważam, że nie powinno to być tak skomplikowane. Czy istnieje czyste rozwiązanie?

+1

Uczciwie kwestionuję sens wychwycenia wyjątku wyrzuconego w * innym * wątku. Czy bieżący wątek musi "dołączyć" do wątku i czekać na zgłoszenie wyjątku? Nie uwzględniłeś tego w pytaniu. – BalusC

+2

@BalusC: Eliminacja wyjątku od wątku roboczego do wątku wywołującego jest powszechnym wymogiem wielu aplikacji. Na przykład aplikacja interfejsu użytkownika może wywoływać wątek SwingWorker, aby wykonać przetwarzanie w tle. Jeśli przetwarzanie zakończy się niepowodzeniem, wyjątek należy przekazać z powrotem do wątku Dispatch zdarzenia. – Adamski

+2

Jest to powszechne wymaganie. Wątek 1 generuje pewną pracę, wykonuje ją za pośrednictwem wątku 2, ale musi wiedzieć, czy się udało, czy nie (tj. Wyrzucił wyjątek). Struktura Executora pomaga ci w tym. –

Odpowiedz

54

Oczywistym rozwiązaniem jest użycie ExecutorService.submit() zamiast execute(). To zwraca państwu Future których można użyć, aby pobrać wynik lub wyjątek zadania:

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(); 
} 
+0

Dzięki, wygląda dokładnie tak, jak jest * zamierzony * być. Czysty. –

+2

Możesz również użyć opcji 'Callable' zamiast' Runnable', a następnie twoje zadanie może wyświetlać sprawdzane wyjątki, a także odznaczać je. – skaffman

+1

getCause zwraca Throwable nie jest wyjątkiem w 1.6 i 1.7 –

6

skaffman ma rację, że za pomocą submit jest najczystszym podejście. Alternatywnym podejściem jest podklasa ThreadPoolExecutor i zastąpienie afterExecute(Runnable, Throwable). Jeśli będziesz postępować zgodnie z tym podejściem , zadzwoń pod numer execute(Runnable) zamiast submit(Runnable) lub afterExecute nie zostanie wywołany.

Per opisu API:

Metoda wywoływana po zakończeniu wykonanie danego Runnable. Ta metoda jest wywoływana przez wątek, który wykonał zadanie . Jeśli non-null, Throwable jest nieprzechwycony RuntimeException lub Error który spowodował wykonanie wypowiedzieć się nagle.

Uwaga: Podczas działania są zamknięte w zadań (takich jak FutureTask) albo bezpośrednio lub za pośrednictwem metod, takich jak przedstawić te zadanie obiektów haczyk i utrzymać obliczeniowych wyjątków i więc nie powodują gwałtownego wypowiedzenia, a wewnętrzne wyjątki to , które nie zostały przekazane do tego sposobu.

8

Udekoruj runnable w inny uruchamialny który łapie wyjątki środowiska wykonawczego i obsługuje je:

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)); 
2

zadania (Callable lub Runnable) poddane ThreadPoolExecutors będzie konwertować do FuturnTask zawiera podpory nazwie callable jest równa zadaniu, które przesyłasz. FuturnTask ma własną metodę run w następujący sposób. Wszystkie wyjątki lub rzuty rzucone w c.call() zostaną przechwycone i umieszczone w podporze o nazwie outcome.Dzwoniąc get metodę FuturnTask, w outcome będą throwed

FuturnTask.run Od Jdk1.8 Kod nieśmiertelności

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); 
      } 
     } 
     ... 
    } 

jeśli chcesz złapać wyjątek:

      1. odpowiedź skaffman za
      2. Zastąp `afterExecute`, gdy tworzysz nowy ThreadPoolExecutor
 @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 
      } 
     } 
Powiązane problemy