After a painful debugging experience, I tracked down this issue: ScheduledThreadPool does not report if a task fails, and does not execute a task that failed once ever again. Therefore, it is hard to keep track of the liveness of periodic jobs, short of checking them with yet other periodic tasks (via dead man’s switch or the ScheduledFuture).
Now we can hand a ScheduledThreadPool an UncaughtExceptionHandler, but not even that seems to work:
import java.util.concurrent.*;
class Test {
public static void main(String[] args) {
final ThreadFactory tf = new ThreadFactory() {
private final ThreadFactory delegate = Executors.defaultThreadFactory();
@Override public Thread newThread(final Runnable r) {
final Thread res = delegate.newThread(r);
res.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(final Thread t, final Throwable e) {
e.printStackTrace();
}
});
return res;
}
};
final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1, tf);
final Runnable task = new Runnable() {
private int c = 0;
@Override
public void run() {
if ( c++ == 5 ) {
throw new ArrayIndexOutOfBoundsException("Runtime error!");
}
System.out.println("Reached " + c);
}
};
exec.scheduleWithFixedDelay(task, 1, 1, TimeUnit.SECONDS);
}
}
The output of this program is simply (Oracle Java SE (64-Bit Server) 1.7.0_06-b24)
Reached 1
Reached 2
Reached 3
Reached 4
Reached 5
and then it hangs (by design).
I can always try-catch the whole task, but that feels ugly; the UncaughtExceptionHandler should do that already!
Is there an API-solution for this issue? Did I do something wrong, or is it a bug?
The currency thread pools capture all exceptions and place then in the Future object for you to inspect.
UncaughtExceptionHandleris only for exception the thread doesn’t catch and kills the thread, which in this case would only be for exception thrown by the thread pool code.A simple way around this is to wrap your runnable.
You can sub-class the ThreadPoolExecutor to do this transparently.
You can also use a cached thread pool to handle exception but this is is more complicated.
One way to use the returned
Futurein a transparent way is to sub-classScheduledThreadPoolExecutor(or any Executor, for that matter):And use it like this:
Now the output is as desired: