English 中文(简体)
How to properly catch RuntimeExceptions from Executors?
原标题:

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?

最佳回答

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();
}
问题回答

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

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

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.

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
            }
        }




相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签