notes-jj notes-jj -4 years ago 156
Java Question

Interrupting Thread with ThreadPoolExecutor and ArrayBlockingQueue not working

I have a main thread, which is interrupted by a shutdown hook.

The main thread has a executor service. If I use a

FixedThreadPool
, everything will work as expected, the main thread itself is interrupted and the threads forked by
FixedThreadPool
are interrupted (edit: In fact they will be interrupted by ThreadPool
.shutdownNow()
/edit). The
boolean
in
Thread.currentThread().isInterrupted()
is true and my routine will be handled as expected.

ThreadPoolExecutor will not interrupt



If I use a
ThreadPoolExecutor
with
ArrayBlockingQueue
or
LinkedBlockingDeque
the interrupt will not work as expected. Just one thread forked by
ThreadPoolExecutor
will be interrupted (edit: Yes, one sub thread will be interrupted when using
Thread.sleep()
in sub thread. Check out the video or source code file ThreadPoolInterruptedSleep /edit). The Executor is not interrupted and the
boolean
behind
ThreadPoolExecutor
will not be true.


  • Why is the
    ThreadPoolExecutor
    acting different than the
    FixedThreadPool
    ?

  • Do I really have to pass the
    ThreadPoolExecutor
    to the shutdown hook, to call the
    .shutdownNow()
    method in the hook? (edit: I'm developing a framwork for our applications and don't what to create a shutdown hook that has to be customized every time. /edit)



Demo Code Edit 2017-03-01



Added a minified version of the code with no extra part. Compile it, run it via command line and interrupt it with

public final class MinThreadPoolInterruptedSleep implements Runnable {
public static void main(final String[] args) {
new Thread(new MinThreadPoolInterruptedSleep()).start();
}

@Override
public void run() {

// Register Shutdown Hook
final Thread mainThread = Thread.currentThread();
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
mainThread.interrupt();
mainThread.join(10000); // Chance for Main Thread to close
} catch (final InterruptedException e) {}
System.out.println("SHUTDOWNHOOK FORCED EXIT");
}));

// final ExecutorService executor = Executors.newFixedThreadPool(2);
final ExecutorService executor = new ThreadPoolExecutor(1,
1, 30, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1),
new ThreadPoolExecutor.CallerRunsPolicy());

for (int i = 0; i < 1E8; i++) {
executor.submit(() -> {
try {
Thread.sleep(1000);
System.out.println("sub tread ran");
} catch (final InterruptedException e) {
System.out.println("SUB THREAD INTERRUPTED");
}
});

if (Thread.currentThread().isInterrupted()) {
System.out.println("MAIN SUBMIT INTERRUPTED");
break;
}
}

executor.shutdown();

try {
executor.awaitTermination(1, TimeUnit.HOURS);
} catch (final InterruptedException e) {}

System.out.println("main force shutdown");
executor.shutdownNow();

try {
executor.awaitTermination(1, TimeUnit.HOURS);
} catch (final InterruptedException e) {}

System.out.println("main finished");
}
}


Demo Code GitHub Edit 2017-02-28



Demo Code can be downloaded at GitHub: demo_java_bug_threadpoolexecutor

For the skeptic I added a short video:
Demo Video

The Solution Edit 2017-03-01



Answer by Calculator
with Reference to the excellent Answer by aioobe

The original Runner Thread:

try {
Thread.sleep(1000);
System.out.println("sub tread ran");
} catch (final InterruptedException e) {
System.out.println("SUB THREAD INTERRUPTED");
}


The modified Runner Thread:

try {
Thread.sleep(1000);
System.out.println("sub tread ran");
} catch (final InterruptedException e) {
System.out.println("SUB THREAD INTERRUPTED");
Thread.currentThread().interrupt();
}


Solution Demo Code on GitHub

Answer Source

The thread that gets interrupted when you use a custom TheadPoolExecutor is the main thread passed to the ShutDownHook In most of the cases the main thread is currently sleeping in one of the runner tasks when the ShutDownHook interrupts due to the CallerRunsPolicy.

The problem is that the sleep within the task will consume the interrupt, meaning it will reset the interrupt flag. Hence, the problem lies in the way you handle the InterruptedException thrown by the sleep within your tasks. Setting the CallerRunsPolicy just reveals the issue. You should reinterrupt the thread by calling Thread.currentThread().interrupt().

From an answer to the question on Handling InterruptedException in Java:

Even though your method can manage to produce a sensible return value in case of an InterruptedException the fact that the thread has been interrupted may still be of importance. In particular, the code that calls your method may be interested in whether an interruption occurred during execution of your method. You should therefore log the fact an interruption took place by setting the interrupted flag: Thread.currentThread().interrupt()

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download