Нещодавно ще раз зустрівся з некоректною обробкою InterruptedException в java. InterruptedException - це checked exception генерується багатьма методами стандартної бібліотеки, які блокують потік виконання. До таких відносяться: interruptible версії lock'ов. метод Thread.sleep (). деякі операції над блокуючими чергами. деякі операції над каналами і інші.
За своєю суттю, InterruptedException сигналізує про те, що потік просять завершити його роботу. При цьому вас не просять негайно завершити свою роботу. Вас просять коректно завершити роботу. На це може знадобиться деякий час.
Переривання потоку здійснюється за допомогою методу Thread.interrupt (). Існує два способи якими JVM повідомляє потік про те, що його переривають. Перший - це власне InterruptedException. Другий - це прапор потоку INTERRUPT. який може бути отриманий за допомогою методу Thread.isInterrupted (). Ігнорування другого методу сигналізування про переривання і є типовою помилкою.
Даний код є невірним, тому що він "тисне" сигнал переривання. Якщо цей код виконується в thread pool'е, то на цьому ТАСК worker (який викликав вас) мав би завершити виконання завдань, так як потік отримав переривання. Але цього не станеться тому що сигнал переривання був перехоплений вищенаведеним кодом. Як правило, це виражається в тому, що після посилки kill сигналу java машині у неї залишаються висіти потоки worker'ов через що JVM не може завершити свою роботу. В даному випадку єдиний вихід - kill -9.
Наступний код так само не є коректним, тому що ми "маскуючи" interrupt в виняткову ситуацію іншого типу. Тим самим він не дає можливості викликає стороні зафіксувати ситуацію переривання.
Коректний код буде виглядати наступним чином.
Тут ми ловимо InterruptedException і виставляємо прапор потоку сигналізує про переривання.
Політика переривання методу є частиною його контракту. Якщо ви декларуєте що InterruptedException не може статися під час виконання методу, але самі при цьому викликаєте методи генеруючі цю виняткову ситуацію, то ви повинні коректно конвертувати InterruptedException у прапор потоку про переривання. Те ж саме відноситься і до викликає стороні. Якщо ви викликаєте метод, який за контрактом генерує InterruptedException. то ви повинні очікувати що метод буде повідомляти вас про переривання за допомогою виняткової ситуації. Якщо немає, то за допомогою прапора.
Уважний читач міг задати собі питання: "А навіщо два способи сигналізації? Хіба не можна обійтися одним? ".
Навіщо потрібен Thread.isInterrupted ()?
InterruptedException буває незручний через те, що це checked exception. Це означає що компілятор змушує вас або обробити його за місцем, або вказати в своєму контракті. У разі, якщо ви не можете вказати InterruptedException в своєму контракті (наприклад, ви імплементіруете інтерфейс де в контракті InterruptedException не вказано), прапор потоку залишається єдиним способом передати викликає стороні інформацію про переривання.
Навіщо потрібен InterruptedException?
InterruptedException дозволяє перервати потік вже виконує блокуючий виклик. У разі, якщо метод вже виконується, то існує тільки один спосіб перервати його виконання без повернення будь-якого значення і не порушуючи при цьому його контракт, - згенерувати виняткову ситуацію. В цьому випадку повертається значення методу просто не визначено.