2013-07-09 65 views
2

此問題來自Brian Goetz第7章7.1.3響應中斷(第143 - 144頁)的書「Java併發實踐」中的示例 它在書中說在循環中調用可中斷阻塞方法

不支持取消但仍然調用可中斷阻塞方法的活動必須在循環中調用它們,在檢測到中斷時重試。在這種情況下,他們應該在本地保存中斷狀態並在返回之前恢復它,如下面的示例所示,而不是立即捕獲InterruptedException。設置中斷狀態太ealry可能會導致一個無限循環,因爲大多數中斷阻塞方法,檢查進入中斷狀態,如果立即將其設置拋出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()拋出一箇中斷的異常,那麼我假設中斷標誌在當前線程上設置正確?然後,下一次調用queue.take()將再次拋出interruptException,因爲當前線程上的前一箇中斷沒有被清除,並且這不會導致無限循環?

回答

1

回答你的第一個問題

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方法不支持cancellation.ie即使線程中斷它不會取消它的任務,並會再次重試。並注意getNextTask方法正在調用可中斷阻塞方法queue.take()它引發intteruptedException。 getNextTask方法將不得不處理中斷異常,因爲它不支持取消並且應該再次重試。 簡而言之,它的方法策略決定重試或者只是拋出方法簽名中斷的異常。

你的第二個問題

Also if queue.take() throws an interruptedException then I am assuming the 
interrupt flag is set on the current thread correct? 

如果沒有中斷拋出異常中斷標誌復位。因爲調用堆棧的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

因爲否則您將不能保證按照方法簽名的要求返回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(); 
    } 
    } 
} 
1

因爲您已經決定getNextTask不會引發異常。當queue.take()不起作用時,唯一要做的就是四處走走並重試。返回null將是拋出異常的道德等價物,並且調用代碼可能不會爲其準備。此方法的唯一出路是具有良好的值或RunTimeException。(看起來對我來說有點極端,但毫無疑問有一個點。)

你實際上並沒有看中斷標誌;它的條件不會影響這段代碼。當然,你將它設置爲調用程序,當它得到它的Task(或RunTimeException)可以知道某事試圖中斷它。 (不要混淆局部變量interrupted和Thread方法interrupted()!)