0

I want to interrupt a thread which is checking for the interrupted flag in each increment of an endless while loop. The only way to exit the loop is by throwing an InterruptedException.

Somewhere in the (inaccessible) code executed by the interruptable thread, a sleep operation is declaring an InterruptedException and a catch block where the exception is swallowed. In an unfortunate case, the thread will be interrupted while the sleep method is being executed, causing the interrupted flag to be reset. In such a case, the thread won't exit the endless while loop.

What would be the best strategy to avoid such a situation? My main thread in the code below calls interrupt in a loop until the interruptable thread is not alive anymore. Is it a reasonable strategy? What are the alternatives?

 public class InterruptableThreads extends Thread {

    public static Thread t1 = new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                while (true) {

                    if (Thread.currentThread().isInterrupted()) {
                        throw new InterruptedException();
                    }

                    try {
                        Thread.sleep(5000);
                    } catch (Exception e) {
                        System.out.println("Swallowing exception. Resetting interrupted flag");
                    }

                    System.out.println("t1 run");
                }
            } catch (InterruptedException e) {
                System.err.println("t1 interrupted.");
            }
        }

    });

    public static void main(String[] args) throws InterruptedException {

        t1.start();

        Thread.sleep(4000);

        while (t1.isAlive()) {
            t1.interrupt();
        }

        t1.join(1000);

        System.out.println("Finished!");


    }
}
  • `catch block where the exception is swallowed.` This is always a bad idea. The correct way to do this is either to throw the exception (not catch it) or to reset the interrupted flag in the catch block. – markspace Feb 16 '18 at 15:17
  • Take a look at https://stackoverflow.com/a/3590008/4505007 – Ananta Feb 16 '18 at 15:19
  • I know it is a bad idea. I don‘t have access to this part of legacy code. – Anton Afanasjew Feb 16 '18 at 16:10
  • did you try it? are you sure the inaccessible code has places which call interruptable methods without swallowing InterruptedException? Or else you would wait forever, and the only way is to call Thread.stop(). – Alexei Kaigorodov Feb 16 '18 at 17:32
  • I’ve tried it, the code definitely swallows the exception and a bad side effect of it is the fact that the ‘interrupted’ flag is reset by this, so my condition does not end the while loop around this code. (This is an extremely improbable constellation and a rare case, that happened once in 5 years) My snippet suggests a solution by repeatingly calling interrupt on the problematic thread in the hope that at one moment it will reach it not being in the sleep section. My question is whether this solution is legit. – Anton Afanasjew Feb 16 '18 at 18:43

2 Answers2

1

The solution you presented, which consists to loop until the thread dies should work. I don't know if this is possible, but you may want to avoid active spinning by including the join statement within that loop:

    while (t1.isAlive()) {
        t1.interrupt();
        try {
            t1.join(1000);

        } catch(InterruptedException e) {
            ...
        }
    }

An alternative could be to isolate the execution of the interruption-swallowing code from the main loop of your interruptible thread:

public static Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<?> f = null;

        try {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    throw new InterruptedException();
                }

                f = executor.submit(new Runnable() {
                    // swallowing code
                });

                try {
                   f.get();

                } catch(CancellationException e) {
                  ...
                } catch(ExecutionException e) {
                  ...
                } // interrupted exceptions will be thrown from get() but not caught here                 

                System.out.println("t1 run");
            }
        } catch (InterruptedException e) {
            System.err.println("t1 interrupted.");
            if (f != null) {
              f.cancel(true);
            }

        } finally {
            executor.shutdownNow();
        }
    }

});

f.get() will give you the possibility to capture interruptions of t1 regardless of the state of the thread spun up by the executor service where the external code is executed and potentially "sleeping".

But adding another thread comes with additional complexity and the risks associated to it.

Alexandre Dupriez
  • 3,026
  • 20
  • 25
  • In fact, I have a join statement in my code. The problem is, it times out before the other thread finishes, or it will freeze forever if there is no timeout. I will analyze the impact of proposed alternative. Thanks. – Anton Afanasjew Feb 17 '18 at 16:34
0

The best strategy is outlined in Java Concurrency in Practice by Brian Goetz. From memory, you re-interrupt the thread from within the catch block, or you throw the exception.

Also, catch the narrowest exception possible, not Exception.

                if (Thread.currentThread().isInterrupted()) {
                    throw new InterruptedException();
                }

                try {
                    Thread.sleep(5000);
                } catch (InterrutedException e) {         // <---- Narrow the scope here
                    System.out.println("Swallowing exception. Resetting interrupted flag");
                    Thread.currentThread().interrupt();   // <----- NEW
                }
markspace
  • 10,621
  • 3
  • 25
  • 39
  • As far as I understood the question, the OP understands what the code should do, but can’t change it. – Holger Feb 16 '18 at 15:23
  • 1
    In that case, the OP is screwed. I'd recommend surreptitiously decompiling and recompiling the byte codes to sneak it in there. Seriously, maybe he should just set the interrupt flag once, and then when the thread doesn't exit point out that the actual error is in the thread code, not his code. – markspace Feb 16 '18 at 15:26
  • I don‘t have access to the exception catching. The question is whether my interrupting approach in the main thread is a valid workaround. – Anton Afanasjew Feb 16 '18 at 16:06