49

Say that I have the following code:

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

Now, if myRunnable throws a RuntimeExcpetion, how can I catch it? One way would be to supply my own ThreadFactory implementation to newSingleThreadExecutor() and set custom uncaughtExceptionHandlers for the Threads that come out of it. Another way would be to wrap myRunnable to a local (anonymous) Runnable that contains a try-catch -block. Maybe there are other similar workarounds too. But... somehow this feels dirty, I feel that it shouldn't be this complicated. Is there a clean solution?

Joonas Pulakka
  • 36,252
  • 29
  • 106
  • 169
  • 1
    Honestly I question the sense of catching an exception thrown in a *different* thread. Does the current thread have to `join` the thread and wait for the exception to be thrown? You didn't cover that in the question. – BalusC Nov 06 '09 at 14:53
  • 2
    @BalusC: Marshalling an exception from a worker thread back onto a calling thread is a common requirement of many applications. For example, a UI application may invoke a SwingWorker thread to do some background processing. If the processing fails the exception needs to be passed back to the Event Dispatch thread. – Adamski Nov 06 '09 at 15:00
  • 2
    It's a common requirement. Thread 1 generates some work, executes it via thread 2, but needs to understand if it's succeeded or not (i.e. thrown an exception). The Executor framework helps you with this. – Brian Agnew Nov 06 '09 at 15:00
  • Umm, actually I hadn't thought about it this far. I was just curious about how, in general, to approach this problem. But people seem to have something cool to say about `submit()` and `Future` below :-) – Joonas Pulakka Nov 06 '09 at 15:00

5 Answers5

60

The clean workaround is to use ExecutorService.submit() instead of execute(). This returns you a Future which you can use to retrieve the result or exception of the task:

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();
}
skaffman
  • 398,947
  • 96
  • 818
  • 769
  • Thanks, looks exactly as the way it's *intended* to be. Clean. – Joonas Pulakka Nov 06 '09 at 17:20
  • 2
    Also, you may want to use `Callable` rather than `Runnable`, then your task can throw checked exceptions as well as unchecked. – skaffman Nov 06 '09 at 17:50
  • 1
    getCause returns a Throwable not an exception in 1.6 and 1.7 – Paul Rubel Jul 25 '13 at 17:46
  • 25
    The problem is that future.get() is blocking, so if you want to be able to run your task asynchronously it's not OK – Loic Jan 20 '15 at 17:15
  • 6
    As commented by @Loic, this solution makes no use as it defeats the whole purpose of using Executors in the first place. – m0skit0 Feb 21 '17 at 16:29
  • @Loic @m0skit0 I don't see the problem. Submit multiple jobs and store their futures (this step behaves like *fork*), after all jobs were submitted, call `get` on the stored futures (if you `get` all futures, this step behaves like a *join*). Drawback: You cannot catch exceptions at the time they were thrown. You have to wait until all jobs terminated (in the worst case). However, [OP only cared about *how*](https://stackoverflow.com/questions/1687977/how-to-properly-catch-runtimeexceptions-from-executors#comment1562723_1687977) but not *when* to catch exceptions. – Socowi Jun 05 '20 at 07:46
11

Decorate the runnable in another runnable which catches the runtime exceptions and handles them:

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));
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
9

Why not call ExecutorService#submit(), get the Future back and then handle possible exceptions yourself when calling Future#get() ?

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
6

skaffman is correct in that using submit is the cleanest approach. An alternative approach is to subclass ThreadPoolExecutor and override afterExecute(Runnable, Throwable). If you follow this approach be sure to call execute(Runnable) rather than submit(Runnable) or afterExecute will not be invoked.

Per the API description:

Method invoked upon completion of execution of the given Runnable. This method is invoked by the thread that executed the task. If non-null, the Throwable is the uncaught RuntimeException or Error that caused execution to terminate abruptly.

Note: When actions are enclosed in tasks (such as FutureTask) either explicitly or via methods such as submit, these task objects catch and maintain computational exceptions, and so they do not cause abrupt termination, and the internal exceptions are not passed to this method.

Adamski
  • 54,009
  • 15
  • 113
  • 152
2

a task(Callable or Runnable) submitted to ThreadPoolExecutors will be convert to a FuturnTask, contains a prop named callable equals the task you submit. FuturnTask has its own run method as follows. All exception or throwable throwed in c.call() will be catched and put into a prop named outcome. When calling FuturnTask's get method, outcome will be throwed

FuturnTask.run From Jdk1.8 Source Code

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

if you want catch the exception :

    1. skaffman's answer
    2. overwrite `afterExecute` when you new a 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
            }
        }
gilon chiu
  • 51
  • 3