newuser
newuser

Reputation: 31

Thread interrupted status is not cleared when InterruptedException is thrown

Can someone explain why this program prints multiple "interrupted" statements.

According to Thread.sleep javadoc

InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Test {

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

        for (int z = 0; z < 100; z++) {

            ExecutorService executor1 = Executors.newSingleThreadExecutor();

            executor1.execute(new Runnable() {

                @Override
                public void run() {

                    ExecutorService executor2 = Executors.newFixedThreadPool(10);

                    for (int i = 0; i < 10; i++) {

                        executor2.execute(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    Thread.sleep(1000 * 100);
                                } catch (InterruptedException e) {
                                    if (Thread.currentThread().isInterrupted()) {
                                        System.out.println("interrupted");
                                    }
                                }
                            }
                        });
                    }

                    executor2.shutdownNow();
                    try {
                        executor2.awaitTermination(5, TimeUnit.SECONDS);
                    } catch (InterruptedException e) {
                    }
                }
            });

            executor1.shutdown();

            try {
                executor1.awaitTermination(10, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
            }
        }
    }
}

output - screenshot

Upvotes: 3

Views: 994

Answers (3)

Ashutosh A
Ashutosh A

Reputation: 1025

THIS ANSWER IS WRONG (keeping it as is so that people know that this code fragment is NOT the cause)

The Executors.newFixedThreadPool() method returns a ThreadPoolExecutor

If you look at ThreadPoolExecutor.shutdownNow(), you'll see that it interrupts the workers potentially twice -

public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers(); //First interrupt here
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate(); //Second interrupt here
        return tasks;
    }

So it must be that some threads are getting interrupted twice.

The first time they get interrupted, they'd come out from sleeping state and throw an InterruptedException. But right after they do that, they are getting interrupted again (before executing your code)

Upvotes: 1

St.Antario
St.Antario

Reputation: 27445

In short:

You see this behavior because you have a race condition here. There is a field ReentrantLock ThreadPoolExecutor::mainLock. The method shutdownNow is guarded by it, but starting the worker thread is not.

To dig a bit deeper:

Take a look at the ThreadPoolExecutor::execute(Runnable). Here is the fragment:

if (isRunning(c) && workQueue.offer(command)) {
    int recheck = ctl.get();
    if (! isRunning(recheck) && remove(command))
        reject(command);
    else if (workerCountOf(recheck) == 0)
        addWorker(null, false);  //add the worker to worker queue and start the thread
}

Now, let's take a look how the worker is started. Here is the runWorker method fragment (Some comments are mine):

// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted.  This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
        (Thread.interrupted() &&
        runStateAtLeast(ctl.get(), STOP))) &&
        !wt.isInterrupted()) //Was not interrupted yet by the shutdownNow method, 
                             //but the pool is being stopped
    wt.interrupt(); //<-- Interrupt it before running. 
//...
try {
      task.run();
}
//...

Shutting down the executor and executing the task is racy in that case.

More specifically worker thread is interrupted before the task execution is started and also before the worker's thread is interrupted by the ExecutorService::shutdownNow method, but after the shutdown was initiated.

If you are unlucky, the interrupted status is being updated by the shutdownNow method before the condition

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

is checke, but after the call to Thread.sleep(1000 * 100); which immediately throws (because already interrupted).

UPD: I inserted System.out.println(Thread.currentThread().isInterrupted()); before the call to Thread.sleep(1000 * 100); and now I cannot reproduce the case.

I can guess that now before calling to Thread::sleep the method ExecutorService::shutdownNow interrupts all thread.

Note: There may well be another causes of the same behavior, but judging by the tests I ran this one seems most probable.

Upvotes: 1

xingbin
xingbin

Reputation: 28289

From the doc

If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.

The status is cleared when you shutdown the executor service during Thread.sleep(1000 * 100) and InterruptedException is thrown.

But the ThreadPoolExecutor re-set the status by t.interrupt(), so you can still get the status.

Here is a piece code of reset status:

    for (Worker w : workers) {
        Thread t = w.thread;
        if (!t.isInterrupted() && w.tryLock()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            } finally {
                w.unlock();
            }
        }
        if (onlyOne)
            break;
    }

Upvotes: 2

Related Questions