> getSecondObject(List firstObject) { Com" /> > getSecondObject(List firstObject) { Com" /> > getSecondObject(List firstObject) { Com"/>

Thread interruption doesn't stop executing infinite task

119 Views Asked by At

I have following snippet of code:

@Async("executor")
    public CompletableFuture<List<SecondObject>> getSecondObject(List<FirstObject> firstObject) {

        CompletableFuture<List<SecondObject>> future = new CompletableFuture<>();
        final int timeout = 5000;
        Timer timer = new Timer();

        //Thread created by configuration
        final Thread executorThread = Thread.currentThread();

        //Method responsible for thread interruption
        final TimerTask interruptTask = new TimerTask() {
            @Override
            public void run() {

                Thread timerTaskThread = Thread.currentThread();
                System.out.println("Exception for thread "+executorThread.getName()+" is thrown");
                System.out.println("Timer task thread "+timerTaskThread.getName()+" is issued");
                executorThread.interrupt();
                future.completeExceptionally(new InterruptedException());
            }
        };
        //Schedule set for thread interruption
        timer.schedule(interruptTask, timeout);
        System.out.println("Thread " + Thread.currentThread().getName() + " is created.");
        System.out.println("Thread " + Thread.currentThread().getName() + " is executing a task.");
        List<SecondObject> secondObject = Collections.emptyList();
        for (int i = 0; i < 5; i++) {
            i--;
        }
        future.complete(secondObject));
        timer.cancel();
        System.out.println("Thread " + Thread.currentThread().getName() + " has been terminated.");

        return future;
    }
}

and the code that tries to access CompletableFuture.

List<FirstObject> firstObject = Collections.emptyList();
List<SecondObject> testedList = null;
CompletableFuture<List<SecondObject>> future = sectionsService.getSecondObject(firstObject);
            try {
                testedList = future.get();
            } catch (InterruptedException | ExecutionException e) {

            }

The problem I have encountered is that when I call get() method then despite having each TimerTask tracking the timeout for each new thread created with @Async, infinite task (here it is infinite for loop) blocks main thread completely. Interrupt flag for @Async thread is thrown but thread itself isn't stopped - program executes infinite for loop despite thread being interrupted. Program waits until get() returns something. I need TimerTask to actually stop thread when timeout is exceeded.

2

There are 2 best solutions below

1
merdon On
future.get(long timeout, TimeUnit unit);
3
Solomon Slow On

executorThread.interrupt() will not stop the thread. All it does is set the thread's isInterrupted() flag.

If you interrupt a thread that happens to be waiting in any one of several different standard library methods (e.g., Thread.sleep(), thread.join(), object.wait()) then the library method will throw an InterruptedException. If you interrupt a thread that happens to be waiting for input, then certain other things happen (see the Javadoc for interrupt().)

But in your program, the executorThread is doing none of those things. It's just doing trivial computations, so no exception will be thrown. If you want the interrupt to be recognized, then you'll have to write code that explicitly checks the flag.

        ...
        for (int i = 0; i < 5; i++) {
            i--;
            if (executorThread.isInterrupted()) {
                break;
            }
        }
        future.complete(secondObject));
        ...

IMO, Correctly using interrupts can be tricky in some situations. Have a look at the tutorial if you want to learn more: https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html