Reputation: 31
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) {
}
}
}
}
Upvotes: 3
Views: 994
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
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
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