0

In service code, I have a ScheduledTaskExecutor that starts a job, then a second thread that will cancel that first thread by interrupting it. The job checks for interrupts intermittently, and when the job gets one, it will throw an InterruptException; the service has a try/catch around that job and the catch handles that interruption. My problem is, the catch block is never hit. The job is definitely being interrupted, clear from logging statements on the job side, Once it throws the InterruptException, it's lost and the service can't catch it.

I tried changing Thread.interrupted() to Thread.currentThread().interrupted(), but it didn't fix the problem.

Here's the server-side code that waits for the InterruptException from the job. The interrupt signal is sent to thread via another thread that's scheduled to run after a timeout. I've verified the job does get the interrupt signal.

    private void run() {
        try {
            job.run();
        } catch (InterruptedException e) {
            log.info("Job was interrupted", e);
        } finally {
            duration.stop();
            timer.record(duration);
        }
    }

Here's how the job checks for interrupts:

    public void checkForInterrupt() throws InterruptedException {
        if (Thread.currentThread().isInterrupted()) {
            logger.info(jobName + " was interrupted");
            throw new InterruptedException(jobName + " has been interrupted");
        }
    }

I'm expecting to see this log line log.info("Job was interrupted", e); The last thing I hear from the thread is a log statement that confirms it's interrupt flag has been set, after which it throws the InterruptedException.

halecommarachel
  • 124
  • 1
  • 9
  • This answer might be useful https://stackoverflow.com/a/3976377/7421645 - if not what does `job.run()` do with the interrupt exception? And how do you know the job is interrupted? – James Apr 15 '19 at 01:14
  • Unclear what you're asking. `ScheduledTaskExecutor` seems to indicate some *deferred* execution. Which thread are you interrupting? What state is it in? Is the "job" already running oder only 'scheduled'? – JimmyB Apr 15 '19 at 09:37
  • @JimmyB So with the ScheduledTaskExecutor, I start the thread to run the job immediately, then schedule another thread to run after a timeout that will interrupt the first thread. The job running in the first thread polls for interrupts with Thread.currentThread().interrupted() every so often and will throw an InterruptedException to end the job execution and let the service know that it was interrupted. – halecommarachel Apr 15 '19 at 20:24

1 Answers1

0

The job is definitely being interrupted, clear from logging statements on the job side, Once it throws the InterruptException, it's lost and the service can't catch it.

This is a FAQ. Whenever the InterruptedException is thrown, the interrupt flag on the thread is cleared. If you need the thread to be stilled interrupted, you need to re-interrupt it. That is always the correct pattern when catching InterruptedException. The reason for it is that if you are writing some sort of library, you don't want to swallow the interrupt flag which would mean that the caller won't know if a thread was interrupted. Interrupting a thread is designed as a graceful shutdown (as opposed to the deprecated stop() method). So propagating the interrupt thread is always a good pattern.

For example:

   ...
} catch (InterruptedException e) {
   // we always should re-interrupt the thread when we catch InterruptedException
   Thread.currentThread().interrupt();
   log.info("Job was interrupted", e);

Then when you get back to the caller you can test the interrupt flag:

if (Thread.currentThread().isInterrupted()) {
   ...

If you want a lot more detailed information about the various methods that throw InterruptedException and the various Thread methods that affect the flag, then see my answer: Methods that Clear the Thread.interrupt() flag

For example, folks should never use Thread.interrupted() because that clears the interrupt flag when it tests it. Your use of Thread.currentThread().isInterrupted() is correct.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • Thanks for the response @Gray. What is the purpose of sending another interrupt to the job that sent the InterruptedException? – halecommarachel Apr 15 '19 at 20:01
  • Oh shoot you know what, I think it's not catching the InterruptedException because the thread executor's .schedule method accepts a Runnable, which doesn't support throwing exceptions. I'll try to run schedule with a Callable. – halecommarachel Apr 15 '19 at 20:27
  • The pattern is good because if you are writing some sort of library, if you swallow the interrupt then callers are never informed the that the thread is interrupted. In your case it may not apply but the good pattern still stands @halecommarachel. – Gray Apr 15 '19 at 21:19