这个问题来自Brian Goetz的“Java并发实践”一书中的一个例子,第7章,7.1.3响应中断(第143-144页) 它在书中说
不支持取消但仍调用可中断阻塞方法的活动必须在循环中调用它们,并在检测到中断时重试。在这种情况下,他们应该在本地保存中断状态并在返回之前恢复它,如下面的示例所示,而不是在捕获InterruptedException时立即恢复。将中断状态设置为太强可能会导致无限循环,因为大多数可中断阻塞方法会检查条目上的中断状态,如果设置了则会立即抛出InterruptedException ......
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();
}
}
我的问题是为什么需要循环?
如果queue.take()抛出一个interruptedException,那么我假设在当前线程上设置了中断标志是否正确?然后下一次调用queue.take()将再次抛出interruptedException,因为当前线程上的上一个中断没有被清除,这不会导致无限循环吗?
答案 0 :(得分:2)
回答你的第一个问题
My question is why is the loop required?
就在这一行
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方法不支持cancel.i.e即使线程被中断,它也不会取消它的任务,并会再次重试。请注意,getNextTask方法正在调用可中断的阻塞方法 queue.take(),它会抛出intteruptedException。 getNextTask方法必须处理interruptedException,因为它不支持取消,应该再次重试。 简单来说,它是一个方法策略,它决定重试或者只是在方法签名中抛出中断的异常。
你的第二个问题
Also if queue.take() throws an interruptedException then I am assuming the
interrupt flag is set on the current thread correct?
否如果抛出中断的异常,则重置中断标志。
另请注意,在finally块中,线程自行中断(Thread.currentThread()。interrupt()),因为getNextTask的调用堆栈也可能调用其他可中断的阻塞方法,这种方法通常首先检查当前是否有线程是否被中断。如果是,则重置中断标志和中断异常 像下面那样抛出。
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);
}
答案 1 :(得分:1)
因为否则您将无法保证按照方法签名的要求返回Task
。没有循环,请考虑以下方法:
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();
}
}
}
答案 2 :(得分:1)
因为你已经决定getNextTask不会抛出异常。当queue.take()
不起作用时,唯一要做的就是到处再试一次。返回null将是抛出异常的道德等价物,并且调用代码可能没有为它做好准备。退出此方法的唯一方法是使用良好的值或RunTimeException。 (对我来说似乎有点极端,但毫无疑问,这有点意义。)
你实际上并没有看到中断标志;它的条件不会影响此代码。你可以将它设置为调用程序,当它获得它的Task
(或RunTimeException)时可以知道某些东西试图中断它。 (不要将局部变量interrupted
与Thread方法interrupted()
混淆!)