Использовать случай для Future.cancel(false)?

В какой ситуации вы хотите передать false для параметра mayInterruptIfRunning в Future.cancel()?

Если я правильно понимаю, если вы проходите false, и задача отменяется, но поток не прерывается, результат (или ExecutionException) никогда не будет доступен, потому что задача по-прежнему отмечена как отмененная (т.е. isCancelled() возвращает true и get() throws CancellationException.)

Другие возможные ситуации:

  • Реализация Runnable или Callable не проверяет прерывания и будет завершена, даже если вы ее прервите (здесь прерывание не имеет значения)
  • Задание уже выполнено до того, как вы вызвали cancel() (снова прерывание не имеет значения)
  • Задача должна выполнить некоторую очистку до ее выхода (хорошо написанная реализация будет использовать try ... finally для этого.)
  • Задача не может быть немедленно завершена и должна продолжать выполнять операции, на которые могут влиять прерывания, например. блокировка ввода-вывода (в этом случае вы, вероятно, не должны вообще вызывать cancel)

Итак, когда/почему вы отменяете задачу, не прерывая ее?

Ответ 1

Если вы боитесь, что прерывание выполнения задачи может оставить ситуацию в плохом состоянии, и вы просто хотите пометить ее как отмененную, чтобы пользователи из Future знали об этом (например, они должны знать запрошенную статистику не были выполнены вовремя).

Написание потокового кода, который правильно обрабатывает прерывания, вообще не является тривиальным, и поэтому можно просто предпочесть его избежать.

Некоторая информация может быть найдена здесь, здесь и конечно, в великой книге Параллельное программирование в Java (от парня, который изначально написал java.util.concurrent).

Ответ 2

ТЛ; дг; Future.cancel(false) полезно только для того, чтобы не запускать уже запущенные задачи.

Есть две важные вещи, которые следует понимать в отношении concurrency и отмены.

Во-первых, в Java аннулирование является чисто кооперативным. Java сигнализирует запрос об аннулировании, поскольку методы блокировки вызывают InterruptedExcetions и устанавливают флаг в Thread. Реализация задачи отвечает за уведомление об аннулировании и отмену. Брайан Гетц объясняет прерывание своего сообщения Работа с InterruptedException. Не все реализации задач корректно обрабатывают прерывание.

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

У вас есть объект Future, но задача может быть в одном из следующих состояний:

  • Ожидание. Например, он может находиться в очереди других задач, ожидающих процессорного времени.
  • Запуск.
  • Завершено.

Если ваша задача находится в первом состоянии "Ожидание", то оба Future.cancel(true) и Future.cancel(false) будут отмечать будущее как отмененное. Задача остается в очереди выполнения задач, но когда исполнитель получает задание, он замечает отмененный флаг и пропускает его.

Если ваша задача находится в третьем состоянии "Завершено", чем оба Future.cancel(true) и Future.cancel(false) возвращают false и ничего не делают. Это имеет смысл, потому что они уже сделаны, и нет способа их отменить.

Флаг mayInterruptIfRunning важен только в том случае, если ваша задача находится во втором состоянии "Запуск".

Если ваша задача запущена, а mayInterruptIfRunning - false, то исполнитель ничего не делает и позволяет завершить задачу.

Если ваша задача запущена, а mayInterruptIfRunning - true, исполнитель прервет задачу. Но помните бит о совместной аннулировании - для прерывания работы задача должна быть реализована для отмены отмены.

Резюме:

Future.cancel(true) подходит, если:

  • Будущее представляет собой долго выполняющуюся задачу, которая, как известно, была реализована для обработки прерывания.

Future.cancel(false) будет правильным:

  • Реализация задачи не может быть прервана.
  • Его неизвестно, если реализация задачи поддерживает отмену.
  • Вы готовы ждать завершения уже запущенных задач.

Ответ 3

У меня есть случай, который может быть вам интересен: у меня есть один поток, который выполняет набор запланированных задач. Одна из задач может быть перенесена либо самой, либо другой задачей.

Для этого я использую Future.cancel(false) в существующей копии в очереди, а затем планирую задачу для нового времени.

Если этот код вызывается из самой запланированной задачи, операция отмены будет no-op, и задача будет запланирована для запуска снова в будущем. Если код вызывается из другого контекста, предстоящая задача еще не запущена, поэтому она будет отменена и заменена заданием, запланированным на новое время.