Перейти к содержимому

Interruptedexception java что это

  • автор:

Interrupted Exception

Недавно еще раз встретился с некорректной обработкой InterruptedException в java. InterruptedException — это checked exception генерируемый многими методами стандартной библиотеки, которые блокируют поток исполнения. К таким относятся: interruptible версии lock’ов, метод Thread.sleep(), некоторые операции над блокирующими очередями, некоторые операции над каналами и другие.

По своей сути, InterruptedException сигнализирует о том, что поток просят завершить его работу. При этом вас не просят немедленно завершить свою работу. Вас просят корректно завершить работу. На это может понадобится некоторое время.

Прерывание потока осуществляется при помощи метода Thread.interrupt(). Существует два способа которыми JVM уведомляет поток о том, что его прерывают. Первый — это собственно InterruptedException . Второй — это флаг потока INTERRUPT , который может быть получен при помощи метода Thread.isInterrupted() . Игнорирование второго метода сигнализирования о прерывании и является типичной ошибкой.

Важно понимать, что interrupt адресуется не только вам (коду, который выполняется в потоке), но и владельцу потока. Другими словами, не только вы в этом потоке заинтересованы в том, чтобы определить что наступило прерывание.

try   Object o = queue.take(); > catch ( InterruptedException e ) <> 

Данный код неверен, потому что он “давит” сигнал прерывания. Если этот код выполняется в thread pool’е, то на этом таске worker (который вызвал вас) должен был бы завершить исполнение задач, так как поток получил прерывание. Но этого не произойдет потому что сигнал прерывания был перехвачен вышеприведенным кодом. Как правило, это выражается в том, что после посылки kill сигнала java машине у нее остаются висеть потоки worker’ов из-за чего JVM не может завершить свою работу. В данном случае единственный выход — kill -9 .

Следующий код так же некорректен, потому что мы “маскируем” interrupt в исключительную ситуацию другого типа. Тем самым он не дает возможности вызывающей стороне зафиксировать ситуацию прерывания.

try   Object o = queue.take(); > catch ( InterruptedException e )   throw new RuntimeException(e); > 

Корректный код будет выглядеть следующим образом.

try   Object o = queue.take(); > catch ( InterruptedException e )   Thread.currentThread().interrupt(); > 

Здесь мы ловим InterruptedException и выставляем флаг потока сигнализирующий о прерывании.

Политика прерывания метода является частью его контракта. Если вы декларируете что InterruptedException не может произойти во время выполнения метода, но сами при этом вызываете методы генерирующие эту исключительную ситуацию, то вы должны корректно конвертировать InterruptedException во флаг потока о прерывании. То же самое относится и к вызывающей стороне. Если вы вызываете метод, который по контракту генерирует InterruptedException , то вы должны ожидать что метод будет уведомлять вас о прерывании посредством исключительной ситуации. Если нет, то посредством флага.

Внимательный читатель мог задать себе вопрос: “А зачем два способа сигнализации? Разве нельзя обойтись одним?”.

Зачем нужен Thread.isInterrupted() ? Link to heading

InterruptedException бывает неудобен по причине того, что это checked exception. Это значит что компилятор заставляет вас или обработать его по месту, или указать в своем контракте. В случае, если вы не можете указать InterruptedException в своем контракте (например, вы имплементируете интерфейс где в контракте InterruptedException не указан), флаг потока остается единственным способом передать вызывающей стороне информацию о прерывании.

Зачем нужен InterruptedException ? Link to heading

InterruptedException позволяет прервать поток уже выполняющий блокирующий вызов. В случае, если метод уже выполняется, то существует только один способ прервать его выполнение без возврата какого-либо значения и не нарушая при этом его контракт, — сгенерировать исключительную ситуацию. В этом случае возвращаемое значение метода просто неопределено.

Объяснение InterruptedException и прерывание потоков

Если бы InterruptedException не было проверенного исключения, вероятно, никто бы даже не заметил этого — что фактически предотвратило бы пару ошибок в течение этих лет. Но поскольку с ним нужно обращаться, многие обращаются с ним неправильно или бездумно. Давайте возьмем простой пример потока, который периодически выполняет некоторую очистку, но большую часть времени находится в спящем режиме.

class Cleaner implements Runnable  Cleaner() final Thread cleanerThread = new Thread(this, "Cleaner"); cleanerThread.start(); >  @Override public void run() while(true) cleanUp(); try TimeUnit.SECONDS.sleep(1); > catch (InterruptedException e) // TODO Auto-generated catch block e.printStackTrace(); > > >  private void cleanUp() //. >  > 

Этот код неверен на многих уровнях!

  1. Запуск Thread в конструкторе может быть плохой идеей в некоторых средах, например, некоторые среды, такие как Spring, будут создавать динамический подкласс для поддержки перехвата методов. В итоге мы получим два потока, запущенных из двух экземпляров.
  2. InterruptedException проглочено, а само исключение не зарегистрировано должным образом
  3. Этот класс запускает новый поток для каждого экземпляра, который он должен использовать ScheduledThreadPoolExecutor вместо этого, разделяемый между многими экземплярами (более надежный и эффективный для памяти)
  4. Кроме того, ScheduledThreadPoolExecutor мы могли бы избежать кодирования спящего / рабочего цикла самостоятельно, а также переключаться на фиксированную скорость, в отличие от представленного здесь поведения с фиксированной задержкой.
  5. И наконец, что не менее важно, нет способа избавиться от этой темы, даже когда на Cleaner экземпляр больше не ссылается что-либо еще.

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

  • Object.wait()
  • Thread.sleep()
  • Process.waitFor()
  • AsynchronousChannelGroup.awaitTermination()
  • Различные методы блокировки в java.util.concurrent.* , например ExecutorService.awaitTermination() , Future.get() , BlockingQueue.take() , Semaphore.acquire() Condition.await() и многие, многие другие
  • SwingUtilities.invokeAndWait()

Обратите внимание, что блокировка ввода / вывода не выбрасывает
InterruptedException (что является позором). Если все эти классы объявляют
InterruptedException , вы можете задаться вопросом, когда это исключение когда-либо выдается?

  • Когда поток заблокирован для объявления какого-либо метода, InterruptedException и вы вызываете Thread.interrupt() такой поток, скорее всего, заблокированный метод немедленно сгенерирует InterruptedException .
  • Если вы отправили задачу в пул потоков ( ExecutorService.submit() ) и вызываете ее Future.cancel(true) во время ее выполнения. В этом случае пул потоков будет пытаться прервать поток, выполняющий такую ​​задачу для вас, эффективно прерывая вашу задачу.

Зная, что на
InterruptedException самом деле означает, мы хорошо подготовлены, чтобы справиться с этим должным образом. Если кто-то пытается прервать наш поток, и мы обнаружили его с помощью перехвата
InterruptedException , самое разумное, что нужно сделать, — позволить этому потоку завершиться, например

class Cleaner implements Runnable, AutoCloseable  private final Thread cleanerThread;  Cleaner() cleanerThread = new Thread(this, "Cleaner"); cleanerThread.start(); >  @Override public void run() try while (true) cleanUp(); TimeUnit.SECONDS.sleep(1); > > catch (InterruptedException ignored) log.debug("Interrupted, closing"); > >  //.  @Override public void close() cleanerThread.interrupt(); > > 

Обратите внимание, что
try-catch блок теперь окружает
while цикл. Таким образом, если
sleep() броски
InterruptedException , мы вырвемся из цикла. Вы можете утверждать, что мы должны записывать
InterruptedException трассировку стека. Это зависит от ситуации, так как в этом случае прерывание потока — это то, что мы действительно ожидаем, а не сбой. Но это зависит от вас. Суть в том, что если
sleep() прервется другим потоком, мы быстро уберемся от
run() всего. Если вы очень осторожны, вы можете спросить, что произойдет, если мы прервем поток, пока он находится в
cleanUp() методе, а не в спящем режиме? Часто вы сталкиваетесь с ручным флагом как это:

private volatile boolean stop = false;  @Override public void run()  >  @Override public void close()  

Однако обратите внимание, что
stop флаг (это должно быть
volatile !) Не будет прерывать операции блокировки, мы должны ждать, пока не
sleep() закончится. С другой стороны, можно утверждать, что явное
flag дает нам лучший контроль, поскольку мы можем отслеживать его значение в любое время. Оказывается, прерывание потока работает так же. Если кто-то прервал поток, когда он выполнял неблокирующие вычисления (например, внутри
cleanUp() ), такие вычисления не прерываются немедленно. Тем не менее, поток помечается как
прерванный, и каждая последующая операция блокировки (например,
sleep() ) просто сбрасывается
InterruptedException немедленно, поэтому мы не потеряем этот сигнал.

Мы также можем воспользоваться этим фактом, если напишем неблокирующий поток, который все еще хочет воспользоваться возможностью прерывания потока. Вместо того, чтобы полагаться на это,
InterruptedException мы просто должны Thread.isInterrupted() периодически проверять
:

public void run() while (Thread.currentThread().isInterrupted()) someHeavyComputations(); > > 

Выше, если кто-то прерывает наш поток, мы откажемся от вычислений, как только вернемся
someHeavyComputations() . Если он будет длиться два долго или бесконечно, мы никогда не обнаружим флаг прерывания. Интересно, что
interrupted флаг не является
одноразовой площадкой . Мы можем позвонить
Thread.interrupted() вместо
isInterrupted() , который сбросит
interrupted флаг, и мы можем продолжить. Иногда вы можете игнорировать прерванный флаг и продолжать работать. В этом случае
interrupted() может пригодиться. Кстати, я (неточно) называю «добытчики», которые
меняют состояние наблюдаемого объекта, «
гейзенгеттерами ».

Обратите внимание на Thread.stop()

Если вы программист старой школы, вы можете вспомнить
Thread.stop() метод, который
устарел уже 10 лет . В Java 8 планировалось
« отменить его » , но в 1.8u5 он все еще есть. Тем не менее, не используйте его и не выполняйте рефакторинг любого кода, использующего
Thread.stop() в
Thread.interrupt() .

Uninterruptibles из гуавы

Редко вы можете игнорировать
InterruptedException вообще. В этом случае взгляните на
Uninterruptibles Гуава. У этого есть много полезных методов как
sleepUninterruptibly() или
awaitUninterruptibly(CountDownLatch) . Просто будь осторожен с ними. Я знаю, что они не объявляют
InterruptedException (что может быть горсткой), но они также полностью предотвращают прерывание текущего потока — что довольно необычно.

Резюме

К настоящему времени я надеюсь, что у вас есть некоторое понимание, почему определенные методы бросают
InterruptedException . Основные выводы:

  • Пойман InterruptedException должен быть обработан должным образом — в большинстве случаев это означает полный разрыв текущей задачи / цикла / потока
  • Глотание InterruptedException редко хорошая идея
  • Если поток был прерван, пока он не находился в блокирующем вызове, используйте isInterrupted() . Также ввод метода блокировки, когда поток уже был прерван, должен немедленно бросить InterruptedException

Обработка InterruptedException в Java

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

Алексей Кодов
Автор статьи
10 июля 2023 в 15:35

Одной из типичных задач в програмировании на Java является работа с потоками и обработка исключений, которые могут возникнуть при их использовании. Например, весьма распространённой является ситуация, когда один поток прерывается другим потоком, и в этом случае возникает исключение типа InterruptedException .

try < // Код, который может вызвать InterruptedException >catch (InterruptedException e) < // Обработка исключения >

Существуют различные способы обработки этого исключения, и выбор конкретного метода зависит от конкретной ситуации и требуемого поведения программы. Рассмотрим два наиболее распространённых подхода.

Первый способ: установка флага прерывания

try < // Код, который может вызвать InterruptedException >catch (InterruptedException e)

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

Второй способ: проброс RuntimeException

try < // Код, который может вызвать InterruptedException >catch (InterruptedException e)

В этом случае при возникновении InterruptedException выбрасывается непроверяемое исключение RuntimeException . Этот подход применяется, когда обработка InterruptedException в текущем контексте невозможна или нежелательна, и требуется прервать выполнение текущего блока кода.

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

Interruptedexception java что это

Spec-Zone .ru

спецификации, руководства, описания, API

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

Платформа Java™
Стандарт Эд. 8

Проект сборка-b92

  • Сводка:
  • Вложенный |
  • Поле |
  • Конструктор |
  • Метод
  • Детально:
  • Поле |
  • Конструктор |
  • Метод

compact1, compact2, compact3

Класс InterruptedException

  • java.lang. Объект
    • java.lang. Throwable
      • java.lang. Исключение
        • java.lang. InterruptedException
        • Все Реализованные Интерфейсы: Сериализуемый

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

        if (Thread.interrupted()) // Clears interrupted status! throw new InterruptedException();

        Сводка конструктора

        Конструкции InterruptedException без сообщения детали.

        Конструкции InterruptedException с указанным сообщением детали.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *