Java долго выполняющаяся задача Прерывание потока против флага отмены - PullRequest
14 голосов
/ 16 декабря 2009

У меня долгое задание, что-то вроде:

public void myCancellableTask() {
    while ( someCondition ) {
       checkIfCancelRequested();
       doSomeWork();
    }
 }

Задание можно отменить (запрашивается отмена, а checkIfCancelRequested () проверяет флаг отмены). Обычно, когда я пишу отменяемые циклы, подобные этому, я использую флаг, чтобы указать, что отмена была запрошена. Но я знаю, что мог бы также использовать Thread.interrupt и проверить, был ли поток прерван. Я не уверен, какой подход был бы предпочтительным и почему, мысли?

спасибо,

Jeff

Ответы [ 4 ]

20 голосов
/ 16 декабря 2009

Одна проблема с использованием прерывания состоит в том, что , если вы не контролируете весь код, выполняемый , вы рискуете, что прерывание не будет работать "должным образом" из-за чьего-либо нарушения понимания того, как обрабатывать прерывания в своей библиотеке. То есть API невидимо экспортирует API вокруг своей обработки interrupt s, от которой вы становитесь зависимыми.

В вашем примере предположим, что doSomeWork был в стороннем JAR и выглядит так:

public void doSomeWork() {
    try { 
        api.callAndWaitAnswer() ; 
    } 
    catch (InterruptedException e) { throw new AssertionError(); }
}

Теперь вам нужно обработать AssertionError (или что-то еще, что может использовать библиотека, которую вы используете). Я видел, как опытные разработчики бросали всякую чепуху при получении прерываний! С другой стороны, возможно, метод выглядел так:

public void doSomeWork() {
    while (true) {
        try { 
            return api.callAndWaitAnswer() ; 
        } 
        catch (InterruptedException e) { /* retry! */ }
    }
}

Эта "неправильная обработка" прерывания заставляет вашу программу зацикливаться бесконечно. Опять же, не отклоняйте это как смешное; Есть много сломанных механизмов обработки прерываний.

По крайней мере, использование собственного флага будет совершенно невидимым для любых сторонних библиотек.

6 голосов
/ 16 декабря 2009

Прерывание вытеснит поток из списка указанных условий ожидания. Ваш собственный флаг отмены не будет. Если вы хотите прерывать ожидания на IO и события, используйте прерывание. В противном случае используйте свой собственный.

1 голос
/ 16 декабря 2009

Это зависит от реализации doSomeWork(). Это чистое вычисление или оно (в любой момент) включает блокировку вызовов API (таких как IO)? Согласно ответу bmargulies , многие блокирующие API в JDK являются прерываемыми и распространяют прерванное исключение вверх по стеку.

Итак, если работа влечет за собой потенциально блокирующие действия, вам нужно будет учитывать прерывания , даже если вы решите управлять процессом, используя флаг, и должны соответствующим образом перехватывать и обрабатывать / распространять прерывания .

Кроме того, если вы полагаетесь на флаг, убедитесь, что ваш флаг объявлен с volatile семантикой.

0 голосов
/ 16 декабря 2009

Я думаю, что это вопрос предпочтения в большинстве случаев. Лично я бы пошел за флаг ручной работы. Это дает вам больше контроля - например, таким образом вы убедитесь, что ваш поток не оставляет какой-либо другой объект в несогласованном состоянии. Кроме того, если производительность действительно критична, имейте в виду, что использование исключений сопряжено с издержками (даже если в 99% случаев оно незначительно).

...