Search code examples
javaexceptionthreadpoolrunnable

Exception in a runnable appears to kill threadpool


I have a runnable that I want to run periodically. On a particular run, I believe the runnable encountered a null pointer, but no exception was shown on the console. After that failed run, it never runs again. I have two questions:

  1. If there was a null pointer, why wasn't this shown on the console
  2. How can I have the scheduled task run again in the future, even if a particular run fails?
scheduler = Executors.newScheduledThreadPool(1);
MyRunnable mr = new MyRunnable(this.data);
scheduler.scheduleWithFixedDelay(mr, 0, STATUS_SENDER_PERIOD, TimeUnit.MILLISECONDS);

Solution

  • Answering your questions,

    1) The reason why you do not see any kind of exception is due to the fact that the FutureTask#setException called within FutureTask#run effectively swallows it. In order to be be able to log the exception you should either create a new class the extends the ScheduledThreadPoolExecutor and override the afterExecute method like so:

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        if (t == null && r instanceof Future<?>) {
            try {
                Object result = ((Future<?>) r).get();
             } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
             } catch (ExecutionException e) {
                t = e;
             }
        }
    
        if (t != null) {
            t.printStackTrace();
        }
    }
    

    Or directly invoke get on the returned ScheduledFuture like so:

    var executor = Executors.newSingleThreadScheduledExecutor();
    var future = executor.scheduleAtFixedRate(new MyRunnable(null), 1, 1, TimeUnit.SECONDS);
    
    try {
        future.get();
    
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
    

    2) The easiest way of re-running the failed runnable would to do this:

    while (true) {
        try {
            future.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
         }
    }
    

    But imho this approach is not the cleanest. Properly coding your Runnable#run method to handle exception would be a better solution.