Jak prawidłowo złapać RuntimeExceptions od wykonawców?

Powiedz, że mam następujący kod:

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

Teraz, jeśli myRunnable rzuca RuntimeExcpetion, Jak mogę go złapać? Jednym ze sposobów byłoby dostarczenie własnej implementacji ThreadFactory do newSingleThreadExecutor() i ustawienie niestandardowych uncaughtExceptionHandler s dla Thread s, które z niej pochodzą. Innym sposobem byłoby zawinięcie myRunnable do lokalnego (anonimowego) Runnable zawierającego try-catch-block. Może są też inne podobne obejścia. Ale... jakoś to jest brudne, czuję, że nie powinno być tak skomplikowane. Czy istnieje czyste rozwiązanie?

Author: Joonas Pulakka, 2009-11-06

5 answers

Czyste obejście polega na użyciu ExecutorService.submit() zamiast execute(). Zwraca Future, którego można użyć do pobrania wyniku lub wyjątku 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();
}
 55
Author: skaffman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-06 14:53:36

Udekoruj runnable w innym runnable, który łapie wyjątki runtime 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));
 9
Author: Aaron Digulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-06 15:02:35

Dlaczego nie zadzwonić ExecutorService#submit(), get the Future wstecz, a następnie samodzielnie obsługiwać ewentualne wyjątki podczas wywoływania Future#get() ?

 9
Author: Brian Agnew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-07 18:44:49

Skaffman ma rację, ponieważ użycie submit jest najczystszym podejściem. Alternatywnym podejściem jest podklasa ThreadPoolExecutor i nadpisać afterExecute(Runnable, Throwable). Jeśli zastosujesz to podejście pamiętaj, aby zadzwonić execute(Runnable) zamiast submit(Runnable) lub afterExecute nie będą wywoływane.

Zgodnie z opisem API:

Metoda wywołana po zakończeniu wykonanie danego Runnable. To metoda jest wywoływana przez wątek, który wykonałem zadanie. If non-null, the Throwable is the uncaught RuntimeException lub Error to spowodowało egzekucja w celu natychmiastowego zakończenia.

Uwaga: Gdy działania są zamknięte w zadania (np. FutureTask) albo jawnie lub za pomocą metod takich jak złożyć, te obiekty zadania złapać i utrzymanie WYJĄTKÓW obliczeniowych oraz więc nie powodują nagłego wypowiedzenie, a wewnętrzne wyjątki są nie przekazywane do tego metoda .

 6
Author: Adamski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-06 15:04:19

Zadanie (Callable lub Runnable) złożone do ThreadPoolExecutors zostanie przekonwertowane na FuturnTask, zawiera prop o nazwie callable równa się zadaniu, które przesyłasz. FuturnTask ma własną metodę run w następujący sposób. Wszystkie wyjątki lub przedmioty rzucane w c.call() zostaną przechwycone i umieszczone w rekwizytorze o nazwie outcome. Podczas wywoływania metody FuturnTask get, outcome zostanie wyrzucona

FuturnTask.Uruchom z kodu źródłowego Jdk1. 8

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. skaffman ' s odpowiedź
      2. zastąp "afterExecute", gdy 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
                }
            }
    
     2
    Author: gilon chiu,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2016-08-25 11:49:00