Я просто смотрел интервью VJUG с Хайнцем Кабуцем, которое вдохновило меня написать пост о прерываниях. Кстати, я бы порекомендовал подписаться на канал VJUG на YouTube — очень информативно.
Хайнц — это всегда хорошая ценность, и трудно смотреть какие-либо его презентации, не многому научившись. Он поднял тему о том, как обращаться с InterruptedException и предположил, что немногие Java-программисты правильно с ним справляются. Лучшее объяснение прерываний потоков, которое я прочитал, содержится в моей любимой книге по Java — параллелизм на практике на практике (p138-144). Если вы прочитали эти страницы, вы будете знать, как правильно обращаться с InterruptedException 🙂
Вот краткое резюме:
Как часто вы сталкивались с этим кодом:
|
1
2
3
4
5
6
7
|
.......try { Thread.sleep(1000);} catch(InterruptedException e){ e.printStackTrace();}...... |
Процесс должен спать на секунду, но «досадно» должен иметь дело с InterruptedException . Разработчик не знает, что делать с этим исключением, поэтому просто регистрирует его на консоли.
Это очень плохая практика! Если вы уверены, что ваш поток никогда не прервется (вы пишете этот код в закрытой системе), то вам, вероятно, следует сделать что-то вроде выброса AssertionError в блоке catch с комментарием, что это никогда не должно произойти. Если вообще возможно, что поток может быть прерван, вам нужно правильно обработать это прерывание.
Поток можно прервать, вызвав его метод interrupt() . Это установит его статус прерывания в true и, следовательно, при вызове isInterrupted() вернет true. Когда interrupt() вызывается определенными методами блокировки, такими как Thread.sleep() InterruptedException . Обратите внимание, что при срабатывании InterruptedException будет установлено на false. В Thread есть метод, называемый interrupted() который, подобно isInterrupted() возвращает состояние прерывания потока, но крайне важно вернуть статус прерывания в значение false. ( interrupted() — метод с очень странным именем для того, что он делает …)
Мы можем увидеть все это на работе в следующем примере:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
package util;/** * Created by daniel on 16/04/15. */public class Interrupt { public static void main(String[] args) { Thread sleeperThread = new Thread(){ public void run(){ try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println(isInterrupted()); //prints false interrupt(); System.out.println(isInterrupted()); //prints true System.out.println(interrupted()); //prints true System.out.println(isInterrupted()); //prints false } } }; sleeperThread.start(); sleeperThread.interrupt(); }} |
Процитируем параллелизм Java на практике:
«В спецификации API или языка нет ничего, что связывало бы прерывание с какой-либо конкретной семантикой отмены, но на практике использование прерывания для чего угодно, кроме отмены, хрупко и трудно поддерживать в больших приложениях».
Другими словами, прерывание — это просто сигнал. Теоретически вы можете использовать механизм прерываний, чтобы указать потоку делать все, что вы хотите, возможно, выполнить действие A вместо B — но мы против этого.
|
1
2
3
4
5
6
7
8
9
|
.......try { Thread.sleep(1000);} catch(InterruptedException e){ actionA(); return;}actionB();...... |
Итак, как правильно бороться с прерыванием. Ну, это немного зависит от вашего кода. Давайте предположим, что мы используем прерывание «правильно» в качестве отмены, и ваш код ожидает отмены (это должно быть указано в документации), тогда ваш код должен отменить свои действия контролируемым образом. То, что выдается исключение, не означает, что вы должны спешно выходить, оставляя позади себя беспорядок Поскольку вы имели дело с прерыванием, нет необходимости восстанавливать состояние прерывания в потоке.
Если вы не ожидаете прерывания, то вам следует изящно обработать прерывание (возможно, завершить то, что вы делаете), а затем восстановить прерывание в потоке для некоторого кода выше по стеку, чтобы иметь дело с ним. Помните, что как только было сгенерировано исключение, статус прерывания устанавливается в false. Вот способ (код взят из книги) относительно того, как это должно быть сделано:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public Task getNextTask(BlockingQueue<Task> queue){ boolean interrupted = false; try{ while(true){ try{ return queue.take(); }catch(InterruptedException e){ interrupted = true; //retry } } }finally { if(interrupted){ Thread.currentThread().interrupt(); } }} |
| Ссылка: | Работа с прерываниями от нашего партнера по JCG Дэниела Шайя в блоге Rational Java . |