Предисловие
Для прерванного экспрессии общий способ обработки - это «проглотить» это - поймать и ничего не делать (или записать, но это не намного лучше) - точно так же, как в списке 4 позже. К сожалению, этот подход игнорирует тот факт, что прерывания могут произойти в течение этого периода, и прерывания могут привести к тому, что приложение потеряет способность отменить активность или закрывать ее во времени.
Метод блокировки
Когда метод бросает прерывание, он не только говорит вам, что он может бросить конкретное исключение проверки, но также говорит вам кое -что другое. Например, он говорит вам, что это метод блокировки, который попытается устранить блокировку и вернуться как можно раньше, если вы ответите должным образом.
Метод блокировки отличается от общего метода, который занимает много времени для работы. Завершение общего метода зависит только от того, что он делает и достаточно ли доступно вычислительные ресурсы (циклы ЦП и память). Завершение метода блокировки также зависит от некоторых внешних событий, таких как истечение срока действия таймера, завершение ввода -вывода или действия другого потока (выпустите блокировку, установите флаг или поместите задачу в рабочую очередь). Общие методы заканчиваются после того, как их работа выполнена, в то время как методы блокировки сложнее предсказать, потому что они зависят от внешних событий. Методы блокировки могут повлиять на отзывчивость, потому что трудно предсказать, когда они закончится.
Метод блокировки не может быть прекращен, потому что он не может ждать, пока оно ожидает события, поэтому очень полезно сделать метод блокировки отмены (и часто очень полезно, если отменяются длительные неблокирующие методы). Отменная операция относится к операции, которая может быть прекращена снаружи перед нормальным завершением. Механизм прерывания, предоставленный потоком и поддерживается Thread.Sleep () и Object.Wait (), является механизмом отмены; Это позволяет одному потоку запросить другой поток, чтобы остановить то, что он делает. Когда метод бросает прерывание, он говорит вам, что, если поток, выполняющий метод, прерывается, он попытается остановить то, что он делает, и вернется заранее, и, бросив прерывание, он возвращается заранее. Блокированный метод блокировки, который должен реагировать на прерывания и бросить прерывание, чтобы его можно было использовать в отменных действиях, не влияя на ответ.
Перерыв потока
Каждый поток имеет логический атрибут, связанный с ним, который представляет прерывистый статус потока. Состояние прерывания неверно в начале; Когда другой поток прерывает поток, вызывая Think.interrupt (), происходит одна из двух ситуаций. Если этот поток выполняет низкоуровневый метод блокировки, такой как Thread.sleep (), Thread.join () или Object.Wait (), он разблокирует и бросит прерывание. В противном случае, Enterrupt () просто устанавливает состояние прерывания потока. После того, как код, работающий в прерванном потоке, может опросить состояние прерывания, чтобы увидеть, просят ли он остановить то, что он делает. Состояние прерывания может быть прочитано Thread.IsErrupted () и может быть прочитано и очищено операцией с именем Thread.Eruprupted ().
Прерывание - это совместный механизм. Когда один поток прерывает другой поток, прерыванный поток не обязательно прекращает то, что он делает немедленно. Вместо этого прерывание - это вежливая просьба к другому потоку, чтобы остановить то, что он делает, когда он готов и удобен. Некоторые методы, такие как Thread.sleep (), очень серьезно относятся к таким запросам, но каждый метод не обязательно отвечает на прерывания. Для запросов на прерывание, методы, которые не блокируют, но все еще требуют много времени для выполнения, могут опросить статус прерывания и вернуться заранее при прерывании. Вы можете игнорировать запросы на прерывание по желанию, но это повлияет на ответ.
Одним из преимуществ совместного характера перерывов является то, что он обеспечивает большую гибкость для безопасной конструкции отмены. Мы редко хотим, чтобы деятельность немедленно прекратилась; Если деятельность отменяется во время проведения обновления, структура данных программы может быть непоследовательной. Прерывание позволяет отменяемой деятельности, чтобы очистить постоянную работу, восстановить инварианты, уведомлять о других действиях, которые она должна быть отменена до ее прекращения.
Ручка прерывания
Если выбрасывание прерывавшего эктриэкцепции означает, что метод является методом блокировки, вызов метода блокировки означает, что ваш метод также является методом блокировки, и у вас должна быть какая -то стратегия для обработки прерывания. Самая простая стратегия, как правило, состоит в том, чтобы самостоятельно бросить прерывание, как показано в коде в методах puttask () и getTask () в списке 1.
Список 1. Не ловить прерванную экспрессию, распространять его на вызывающего абонента
открытый класс TaskQueue {Private Static Final int max_tasks = 1000; частная блокировка <task> queue = new LinkedBlockingqueue <sass> (max_tasks); public void puttask (задача r) бросает прерванное эктрипений {queue.put (r); } публичная задача getTask () бросает прерванную эксплуацию {return queue.take (); }} Иногда требуется некоторые работы по очистке, прежде чем исключение будет распространено. В этом случае вы можете поймать прерывавшего эктри, выполнить очистку, а затем бросить исключение. Листинг 2 демонстрирует эту технику, механизм, используемый для совпадения игроков в онлайн -игровых услугах. Метод MatchPlayers () ждет прибытия двух игроков, а затем запустит новую игру. Если метод прерывается, когда прибыл один игрок, но другой игрок не прибыл, он вернет этого игрока обратно в очередь и повторно прервалоэксация, чтобы запрос игрока на игру не был потерян.
Список 2. Выполнить работу по очистке конкретной задачи перед повторным перерывом
Public Class Playermatcher {Private Playerce Players; Public Playermatcher (Playerource Players) {this.Players = Players; } public void MatchPlayers () Throws прерывания {try {PlayerOne, playertwo; while (true) {playerone = playertwo = null; // ждать, пока два игрока прибудут и запустите новый Game PlayerOne = Player.WaitForPlayer (); // может бросить т.е. playertwo = players.waitforplayer (); // может бросить IE Startnewgame (PlayerOne, Playertwo); }} catch (прерванное разумное восприятие e) {// Если мы получили одного игрока и были прерваны, верните этого игрока, если (PlayerOne! = null) Players.Addfirst (PlayerOne); // затем распространять исключение броска E; }}} Не переставайте глотать живым
Иногда бросание прерванного эктриэкцепции не подходит, например, когда задача, определяемая выполняемой вызовой, прерывим. В этом случае прерывание не может быть переоборудовано, но вы тоже не хотите ничего делать. Когда метод блокировки обнаруживает прерывание и бросает прерывание, он очищает состояние прерывания. Если прерываноэкспений поймана, но не может быть переоборудовано, то доказательства прерывания должны быть сохранены, чтобы код более высокого уровня в стеке вызовов мог знать прерывание и ответить на него. Эта задача может быть выполнена, вызывая Enterrupt (), чтобы «повторно преодолеть» текущий поток, как показано в списке 3. По крайней мере, всякий раз, когда попадает в прерывание, и оно не повторно, текущий поток переворачивается перед возвращением.
Листинг 3. Возобновление прерванного состояния после захвата прерванного
открытый класс TaskRunner реализует runnable {private blockqueue <task> queue; Public TaskRunner (Blockqueue <Task> queue) {this.queue = queue; } public void run () {try {while (true) {task task = queue.take (10, timeUnit.seconds); task.execute (); }} catch (прерывание Exception e) {// восстановить прерыванный поток состояния. CurrentThread (). Enterrupt (); }}} Худшее, что нужно сделать при работе с прерыванием,-это проглотить его необработанным-поймать, а затем не переориентировать и не переоценить состояние прерывания потока. Для исключения, с которым вы не знаете, как справиться, наиболее стандартный способ обработки-это поймать его и записать, но этот метод все еще похож на необработанное прерывание, потому что код более высокого уровня в стеке вызовов все еще не может получить информацию об исключении. (Не целесообразно просто записывать прерывание, потому что уже слишком поздно обрабатывать его, когда кто -то приходит к чтению журнала.) В списке 4 показан широко используемый шаблон, который также является шаблоном необработанного прерывания глотания:
Список 4. Сырой прерывание глотания - не делайте этого
// Не делайте этот открытый класс TaskRunner реализует runnable {private blockqueue <sase> queue; Public TaskRunner (Blockqueue <Task> queue) {this.queue = queue; } public void run () {try {while (true) {task task = queue.take (10, timeUnit.seconds); task.execute (); }} catch (прерывание поглощено) { / * Не делайте этого - восстановить прерыванный статус вместо * /}}} Если прерывание не может быть переоборудовано, независимо от того, планируете ли вы обработать запрос на прерывание или нет, вам все равно необходимо повторно включить текущий поток, так как один запрос прерывания может иметь несколько «приемников». Стандартная реализация потока Pool Thread (ThreadPoolexeCutor) отвечает за прерывание, поэтому прерывание задачи в пуле запущенных потоков может иметь двойной эффект. Одним из них является отмена задачи, а другой - уведомить поток выполнения, что пул потоков должен быть закрыт. Если задача поглощает запрос, поток работника не будет знать, что существует запрошенное прерывание, задерживая закрытие приложения или услуги.
Реализовать отмененные задачи
Не существует конкретной семантики для прерываний в спецификации языка, но в более крупных программах трудно поддерживать семантику прерывания, кроме отмены. В зависимости от того, что такое деятельность, пользователи могут запросить отмену с помощью графического интерфейса или с помощью сетевого механизма, такого как JMX или веб -сервис. Логику программы также можно попросить отменить. Например, веб -гусеницы автоматически отключатся, если он обнаружит, что диск заполнен, в противном случае параллельный алгоритм запустит несколько потоков для поиска различных областей пространства решения, и отменит эти потоки, как только один из потоков найдет решение.
То, что задача отменяется, не означает, что запрос прерывания должен быть немедленно отреагировать. Для задач, которые выполняют код в цикле, обычно необходимо проверить на наличие прерываний один раз на каждую итерацию цикла. В зависимости от того, как долго выполняется цикл, для любого кода может потребоваться некоторое время, чтобы заметить, что поток был прерван (либо опрашивает состояние прерывания, вызывая метод потока. Если задача должна быть отзывчивой, она может чаще опросить состояние прерывания. Метод блокировки обычно сразу же оправляет состояние прерывания на входе и, если он настроен на улучшение отзывчивости, он также бросает прерывание.
Единственный раз, когда вы можете перестать глотать живым, это то, что вы знаете, что поток собирается выйти. Этот сценарий возникает только тогда, когда класс, который вызывает прерывистый метод, является частью потока, а не запускаемого или общего кода библиотеки, как показано в листинге 5. Список 5 создает поток, в котором перечислены основные числа, пока он не будет прерван, который также разрешается выходить при перерыве. Цикл, используемый для поиска простых проверок для перерывов в двух местах: одно из них заключается в опросе метода isErrupted () в головке цикла, а другой должен вызвать метод блокировки blockqueue.put ().
Список 5. Если вы знаете, что поток собирается выйти, вы можете проглотить прерывание
primeproducer primeproducer Extends {Private Final Blockingqueue <Biginteger> очередь; Primeproducer (blockqueue <biginteger> очередь) {this.queue = queue; } public void run () {try {bigInteger p = bigInteger.one; while (! thread.currentThread (). isErengeTrupted ()) queue.put (p = p.nextprobableprime ()); } catch (прерывание потребляемого) { / * разрешить поток выйти * /}} public void cancel () {rentrupt (); }} Нерушимый метод блокировки
Не все методы блокировки бросают прерывание. Входные и выходные классы классов, ожидающие завершения ввода -вывода, но они не бросают прерывание и не вернутся заранее, если прерван. Тем не менее, для гнезда ввода -вывода, если нить закрывает гнездо, блокирующая операция ввода -вывода на этом розетке закончится рано, и будет выброшена разъем. Не блокирующие классы ввода-вывода в java.nio не поддерживают прерывистый ввод-вывод, но они также могут быть разблокированы, закрыв канал или запрашивая пробуждение на селекторе. Аналогичным образом, попытка приобрести внутреннюю блокировку (ввод в синхронизированный блок) не может быть прервана, но Reentrantlock поддерживает режим прерываемого сбора.
Неисчислимые задачи
Некоторые задачи отказываются от прерывания, что делает их неизбежными. Тем не менее, даже не канкуляционные задачи должны попытаться сохранить состояние прерывания в случае, если более высокий код в стеке вызовов необходимо для обработки прерываний после того, как не поддающиеся задачам закончились. В листинге 6 показан метод, который ожидает блокирующего очередь, пока в очереди не появится доступный элемент, независимо от того, прерывается ли он или нет. Для удобства он возобновляет состояние прерывания после окончания в наконец -то блоке, чтобы не лишить вызывающего абонента запроса прерывания. (Он не может возобновить состояние прерывания раньше, потому что это приведет к бесконечному петлю - blockqueue.take () немедленно опросить состояние прерывания у входа, и, если будет найден набор состояния прерывания, будет выброшено прерывание.
Перечисление 6. Необеспеченные задачи, которые восстанавливают прерванное состояние до возвращения
Общественная задача GetNextTask (Blockqueue <Task> queue) {Boolean прерывается = false; try {while (true) {try {return queue.take (); } catch (прерванная экспрессия e) {прерывание = true; // провалиться и повторить}}} наконец {if (прервано) Thread.currentThread (). Enterrupt (); }} Суммировать
Вы можете использовать механизм прерывания совместной работы, предоставленный платформой Java для создания гибких стратегий отмены. Действия могут по своему усмотрению решать, отменяются ли они или не отбираются, и как реагировать на прерывания, а также могут отложить прерывания, если немедленное возвращение поставит под угрозу целостность приложения. Даже если вы хотите полностью игнорировать прерывания в своем коде, вы должны убедиться, что состояние прерывания восстанавливается без перепродажи его, чтобы код, вызывающий его, не знает, что происходит прерывание. Вышеуказанное - полное содержание теории Java и практики обработки исключений прерывания. Я надеюсь, что эта статья будет вам полезна. Если у вас есть какие -либо вопросы, пожалуйста, оставьте сообщение для обсуждения.