Java long running task. Прерывание потока и флажок отмены.

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

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

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

спасибо,

Джефф

Ответы

Ответ 1

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

Ответ 2

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

В вашем примере предположим, что 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! */ }
    }
}

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

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

Ответ 3

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

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

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

Ответ 4

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