2013-07-09 3 views
2

Этот вопрос взят из примера из книги «Параллельность Java на практике» Брайана Гетца, глава 7, 7.1.3. Ответ на прерывание (стр. 143 - 144) В книге говорится о книгеВызов методов прерывистой блокировки в цикле

Действия, которые не поддерживают отмену, но продолжают прерывать методы блокировки, должны будут вызвать их в цикле, повторив при обнаружении прерывания. В этом случае они должны сохранять статус прерывания локально и восстанавливать его непосредственно перед возвратом, как показано в примере ниже, а не сразу после обнаружения 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() выбрасывает interruptedException, то я предполагаю, что флаг прерывания установлен на текущий поток правильно? Затем следующий вызов queue.take() снова вызовет interruptedException, поскольку предыдущее прерывание на текущем потоке не очищается, и это не вызовет бесконечный цикл?

ответ

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 должен обрабатывать 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

Потому что в противном случае вам не гарантируется возврат 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()!)

Смежные вопросы