Exception handling in ThreadPools

2019-01-31 06:38发布

问题:

I have a ScheduledThreadPoolExecutor that seems to be eating Exceptions. I want my executor service to notify me if a submitted Runnable throws an exception.

For example, I'd like the code below to at the very least print the IndexArrayOutOfBoundsException's stackTrace

threadPool.scheduleAtFixedRate(
  new Runnable() {
    public void run() {
      int[] array = new array[0];
      array[42] = 5;
    }
  },
  1000,
  1500L,
  TimeUnit.MILLISECONDS);

As a side question. Is there a way to write a general try catch block for a ScheduledThreadPoolExecutor?

//////////END OF ORIGINAL QUESTION //////////////

As suggested the following Decorator works well.

public class CatcherTask implements Runnable{

    Runnable runMe;

    public CatcherTask(Runnable runMe) {
        this.runMe = runMe;
    }

    public void run() {
        try {
            runMe.run();
        } catch (Exception ex){
            ex.printStackTrace();
        }
    }
}

回答1:

I wrote a small post about this problem a while ago. You have two options:

  1. Use the solution provided by Colin Herbert or
  2. use a modified version of Mark Peters solution but instead of assigning a UncaughtExceptionHandler you wrap each submitted runnable into a runnable of your own which executes (calls run) the real runnable inside a try-catch-block.

EDIT
As pointed out by Mark, it's important to wrap the Runnable passed to ScheduledExecutorService instead of the one passed to the ThreadFactory.



回答2:

Warning: This method is not applicable to scheduled thread pool executors. This answer has been undeleted for its relevance to other thread pool executors. See Willi's answer.

Override the ThreadFactory to give Threads an UncaughtExceptionHandler:

ThreadPoolExecutor exec = new ThreadPoolExecutor...;

exec.setThreadFactory(new ExceptionCatchingThreadFactory(exec.getThreadFactory()));
//go on to submit tasks...


private static class ExceptionCatchingThreadFactory implements ThreadFactory {
    private final ThreadFactory delegate;

    private ExceptionCatchingThreadFactory(ThreadFactory delegate) {
        this.delegate = delegate;
    }

    public Thread newThread(final Runnable r) {
        Thread t = delegate.newThread(r);
        t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                e.printStackTrace();  //replace with your handling logic.
            }
        });
        return t;
    }
}


回答3:

You can use the get() method from the Future you're getting by calling scheduleAtFixedRate(). It will throw an ExecutionException if an exeception occurred during the thread execution.



回答4:

You could also use a ThreadPoolTaskScheduler from the Spring Framework, which exposes a method to set an error handler and does all the wrapping for you. The default behavior depends on the type of task:

If the provided ErrorHandler is not null, it will be used. Otherwise, repeating tasks will have errors suppressed by default whereas one-shot tasks will have errors propagated by default since those errors may be expected through the returned Future. In both cases, the errors will be logged.

If you only want to use the wrapping part and not the TaskScheduler you can use

TaskUtils.decorateTaskWithErrorHandler(task, errorHandler, isRepeatingTask)

which the TaskScheduler uses internally.



回答5:

You can subclass ScheduledThreadPoolExecutor and override the afterExecute method to handle exceptions and errors for any kind of Runnable that you submit.



回答6:

Consider adding a static event in your ScheduledThreadPoolExecutor class that any of your tasks can call if an exception is thrown. That way, you can leverage that event to capture and handle the exceptions that occur within your threads.