Why Do We Need Thread.currentThread().interrupt() in Interruptible Methods?
Let's evaluate why we need Thread.currentThread().interrupt() in interruptible methods.
Join the DZone community and get the full member experience.
Join For FreeBy an interruptible method, we mean a blocking method that may throwInterruptedException
, for example, Thread.sleep()
, BlockingQueue.take()
, BlockingQueue.poll(long timeout, TimeUnit unit)
, and so on. A blocking thread is usually in a BLOCKED, WAITING, or TIMED_WAITING state, and if it is interrupted, then the method tries to throw InterruptedException
as soon as possible.
Since InterruptedException
is a checked exception, we must catch it and/or throw it. In other words, if our method calls a method that throws InterruptedException
, then we must be prepared to deal with this exception. If we can throw it (propagate the exception to the caller), then it is not our job anymore. The caller has to deal with it further. So, let's focus on the case when we must catch it. Such a case can occur when our code is run inside Runnable
, which cannot throw an exception.
You may also like: Java Thread Tutorial: Creating Threads and Multithreading in Java
Let's start with a simple example. Trying to get an element from BlockingQueue
via
poll(long timeout, TimeUnit unit)
can be written as follows:
try {
queue.poll(3000, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
...
logger.info(() -> "Thread is interrupted? "
+ Thread.currentThread().isInterrupted());
}
Attempting to poll an element from the queue can result in an InterruptedException
. There is a window of 3,000 milliseconds in which the thread can be interrupted. In case of interruption (for example, Thread.interrupt()
), we may be tempted to think that calling Thread.currentThread().isInterrupted()
in the catch block will return true
.
After all, we are in an InterruptedException
catch block, so it makes sense to believe this. Actually, it will return false
, and the answer is in the source code of the poll(long timeout, TimeUnit unit)
method listed as follows:
1: public E poll(long timeout, TimeUnit unit)
throws InterruptedException {
2: E e = xfer(null, false, TIMED, unit.toNanos(timeout));
3: if (e != null || !Thread.interrupted())
4: return e;
5: throw new InterruptedException();
6: }
More precisely, the answer is in line 3. If the thread was interrupted, then Thread.interrupted()
will return true
and will lead to line 5 ( throw new InterruptedException()
). But beside testing, if the current thread was interrupted, Thread.interrupted()
clears the interrupted status of the thread. Check out the following succession of calls for an interrupted thread:
Thread.currentThread().isInterrupted(); // true
Thread.interrupted() // true
Thread.currentThread().isInterrupted(); // false
Thread.interrupted() // false
Notice that Thread.currentThread().isInterrupted()
tests whether this thread has been interrupted without affecting the interrupted status.
Now, let's get back to our case. So, we know that the thread was interrupted since we caught InterruptedException
, but the interrupted status was cleared by Thread.interrupted()
. This means also that the caller of our code will not be aware of the interruption.
It is our responsibility to be good citizens and restore the interrupt by calling the interrupt()
method. This way, the caller of our code can see that an interrupt was issued and act accordingly. The correct code could be as follows:
try {
queue.poll(3000, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
...
Thread.currentThread().interrupt(); // restore interrupt
}
As a rule of thumb, after catchingInterruptedException
, do not forget to restore the interrupt by callingThread.currentThread().interrupt()
.
Let's tackle a problem that highlights the case of forgetting to restore the interrupt. Let's assume a Runnable
that runs as long as the current thread is not interrupted (for example, while (!Thread.currentThread().isInterrupted()) { ... }
).
At each iteration, if the current thread interrupted status is false
, then we try to get an element from BlockingQueue
.
The following code is the implementation:
Thread thread = new Thread(() -> {
// some dummy queue
TransferQueue<String> queue = new LinkedTransferQueue<>();
while (!Thread.currentThread().isInterrupted()) {
try {
logger.info(() -> "For 3 seconds the thread "
+ Thread.currentThread().getName()
+ " will try to poll an element from queue ...");
queue.poll(3000, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
logger.severe(() -> "InterruptedException! The thread "
+ Thread.currentThread().getName() + " was interrupted!");
Thread.currentThread().interrupt();
}
}
logger.info(() -> "The execution was stopped!");
});
As a caller (another thread), we start the above thread, sleep for 1.5 seconds, just to give time to this thread to enter in the poll()
method, and we interrupt it. This is shown in the following code:
thread.start();
Thread.sleep(1500);
thread.interrupt();
This will lead to InterruptedException
.
The exception is logged and the interrupt is restored.
At the next step, while
evaluates Thread.currentThread().isInterrupted()
to false
and exits.
As a result, the output will be as follows:
[18:02:43] [INFO] For 3 seconds the thread Thread-0
will try to poll an element from queue ...
[18:02:44] [SEVERE] InterruptedException!
The thread Thread-0 was interrupted!
[18:02:45] [INFO] The execution was stopped!
Now, let's comment on the line that restores the interrupt:
...
} catch (InterruptedException ex) {
logger.severe(() -> "InterruptedException! The thread "
+ Thread.currentThread().getName() + " was interrupted!");
// notice that the below line is commented
// Thread.currentThread().interrupt();
}
...
This time, the while
block will run forever since its guarding condition is always evaluated to true
.
The code cannot act on the interruption, so the output will be as follows:
[18:05:47] [INFO] For 3 seconds the thread Thread-0
will try to poll an element from queue ...
[18:05:48] [SEVERE] InterruptedException!
The thread Thread-0 was interrupted!
[18:05:48] [INFO] For 3 seconds the thread Thread-0
will try to poll an element from queue ...
...
As a rule of thumb, the only acceptable case when we can swallow
an interrupt (not restore the interrupt) is when we can control the entire call stack (for example, extend Thread).
Otherwise, catchingInterruptedException
should contain
Thread.currentThread().interrupt()
as well.
Done! The complete code is available on GitHub.
If you enjoyed this article, then I am sure that you will love my book Java Coding Problems, which contains two chapters dedicated to Java concurrency problems.
Further Reading
Java Thread Tutorial: Creating Threads and Multithreading in Java
Opinions expressed by DZone contributors are their own.
Comments