2

这个问题来自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,因为当前线程上的上一个中断没有被清除,这不会导致无限循环吗?

4

3 回答 3

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 方法不支持取消。即即使线程被中断,它也不会取消它的任务并会重试。请注意,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);
        }
于 2013-07-09T14:15:02.857 回答
1

因为否则您将无法保证按照Task方法签名的要求返回 a 。如果没有循环,请考虑以下方法:

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();
    }
  }
}
于 2013-07-09T13:40:55.027 回答
1

因为你已经决定 getNextTask 不会抛出异常。当queue.take()不起作用时,唯一要做的就是四处走动再试一次。返回 null 在道德上等同于抛出异常,并且调用代码可能没有为此做好准备。摆脱此方法的唯一方法是使用良好的值或 RunTimeException。(对我来说似乎有点极端,但毫无疑问这是有道理的。)

您实际上并没有查看中断标志;它的条件不会影响此代码。您正在仔细地设置它,以便调用程序在它确实得到它Task(或 RunTimeException)时可以知道有什么东西试图中断它。(不要将局部变量interrupted与 Thread 方法混淆interrupted()!)

于 2013-07-09T14:15:04.293 回答