Я просто смотрел интервью 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 . |