Reputation: 488
This question is from an example from the book "Java concurrency in Practice" by Brian Goetz, Chapter 7 , 7.1.3 Responding to interruption (page 143 - 144) It says in the book
Activities that do not support cancellation but still call interruptible blocking methods will have to call them in a loop, retrying when interruption is detected. In this case, they should save the interruption status locally and restore it just before returning as shown in example below, rather than immediately upon catching InterruptedException. Setting the interrupted status too ealry could result in an infinite loop, because most interruptible blocking methods check the interrupted status on entry and throw InterruptedException immediately if it is set......
public Task getNextTask(BlockingQueue<Task> queue) {
boolean interrupted = false;
try {
while (true) {
try {
return queue.take();
} catch (InterruptedException e) {
interrrupted = true;
}
}
} finally {
if (interrupted)
Thread.currentThread().interrupt();
}
}
My question is why is the loop required?
Also if queue.take() throws an interruptedException then I am assuming the interrupt flag is set on the current thread correct? Then the next call to queue.take() will again throw interruptedException since the previous interrupt on current thread is not cleared and will this not cause an infinite loop?
Upvotes: 2
Views: 709
Reputation: 2444
Answer to your first question
My question is why is the loop required?
lies in this line
Activities that do not support cancellation but still call interruptible
blocking methods will have to call them in a loop, retrying when interruption
is detected.
From Java concurrency in Practice 7.1.3
getNextTask method doesn't support cancellation.i.e even if the thread is interrupted it will not cancel it task and will retry again. And note that getNextTask method is calling a interruptible blocking method queue.take() which throws intteruptedException. getNextTask method will have to handle interruptedException as it doesn't support cancellation and should retry again. In simple words its a policy of method which decides either retry or else just throw the interrupted exception in method signature.
Your second question
Also if queue.take() throws an interruptedException then I am assuming the
interrupt flag is set on the current thread correct?
No if interrupted exception is thrown the interrupted flag is reset.
Also note in the finally block, the thread is self interrupting itself (Thread.currentThread().interrupt()), because the calling stack of getNextTask may also call other interruptible blocking methods, Such methods normally first check if the current thread has been interrupted or not. If yes the interrupted flag is reset and an Interrupted exception is thrown like the one below.
Below is the Code from AQS(AbstractQueueSynchronizer) from java
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
Upvotes: 2
Reputation: 3158
Because you've decided that getNextTask will throw no exceptions. When queue.take()
doesn't work, the only thing to do is go around and try again. Returning a null would be the moral equivalent of throwing an exception, and the calling code may not be prepared for it. The only way out of this method is with a good value or a RunTimeException. (Seems a bit extreme to me, but no doubt there's a point to it.)
You're not actually looking at the interrupt flag; it's condition doesn't affect this code. You are, thoughfully, setting it so the calling program, when it does get its Task
(or RunTimeException) can know that something tried to interrupt it. (Don't confuse the local variable interrupted
with the Thread method interrupted()
!)
Upvotes: 1
Reputation: 12766
Because otherwise you would not be guaranteed to return a Task
, as required by your method signature. Without the loop, consider the method:
public Task getNextTask(BlockingQueue<Task> queue) {
boolean interrupted = false;
try {
return queue.take();
} catch (InterruptedException e) {
interrupted = true;//No return here, the compiler will complain
} finally {
if(interrupted) {
Thread.currentThread().interrupt();
}
}
}
Upvotes: 1