Главная » Java » Корректное завершение работы потока Java

0

Нередки случаи, когда поток создается для достижения определенной цели, а затем его выполнение необходимо прервать прежде, чем он решит поставленную задачу. В качестве наиболее простого примера можно привести ситуацию, когда пользователь щелкает мышью на кнопке Отмена, желая Принудительно остановить процесс вычислений. Для того чтобы обеспечить возможность управляемого завершения работы потока, программисту необходимо приложить определенные усилия, но соответствующий механизм, к счастью, прост и надежен. Право на завершение потока запрашивается с помощью вызова метода interrupt, и код соответствующего потока должен сам следить за событием прерывания и отвечать за его выполнение. Рассмотрим пример.

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

Использование interrupt полезно и оправданно также в тех случаях, когда необходимо обеспечить работающий поток средствами отслеживания происходящих событий. Пусть, например, в цикле обновления экранной информации требуется выполнять обращение к базе данных посредством транзакции и при этом обслуживать интерфейс пользователя (скажем, проверять обращение к кнопке Отмена) после нормального завершения транзакции. В потоке, ответственном за поддержку пользовательского интерфейса, нетрудно предусмотреть обработку события щелчка на кнопке Отмена с обращением к методу interrupt объекта потока, выполняющего обновление данных. Последний, завершая транзакцию, Должен проверить, вызывался ли метод interrupt, и завершить свое выполнение, если событие прерывания действительно произошло.

Перечислим методы, имеющие отношение к механизму прерывания работы потока: (1) interrupt – посылает потоку уведомление о прерывании; (2)  SInterrupted – проверяет, была ли прервана работа потока вызовом метода lnterrupt; (3) interrupted – статический метод, проверяющий, выполнялось ли Прерывание текущего потока, и очищающий "состояние прерывания" потока. Последнее может быть очищено только самим потоком – "внешних" способов отмены уведомления о прерывании, посланного потоку, не существует. Вообще говоря, особого смысла в проверке состояния прерывания другого потока попросту нет, поэтому названные методы обычно применяются в контексте текущего Потока. Когда поток обнаруживает посланное уведомление о прерывании, ему зачастую необходимо, прежде чем отреагировать на сигнал, выполнить определенные вспомогательные операции. Такие операции могут быть сопряжены с действиями, на которые оказывает влияние необработанное состояние прерывания, поэтому потоки, обнаруживая уведомление прерывания, очищают состояние с помощью метода interrupted.

 

Прерывание посредством метода interrupt обычно не воздействует на работоспособность потока, но некоторые методы, такие как sleep и wait, будучи прерванными, выбрасывают исключение типа InterruptedException. Другими словами если поток в момент прерывания его работы с помощью interrupt выполняет один из этих методов, они генерируют исключение InterruptedException. В этом случае состояние прерывания потока очищается, поэтому код, обрабатывающий исключение InterruptedException, обычно должен выглядеть следующим образом:

 

void tick(int count, long pauseTime) {

try {

 

for (int i = о; i < count; i++) {

 System.out.println(‘. ‘);

System.out.f1ush();

 Thread.sleep(pauseTime);

            }

} catch (InterruptedException е) {

 Thread.currentThread().interrupt();

}

}

Метод tick выводит на экран символ точки count раз, "засыпая" после каждой операции на период времени, равный значению pauseTime, выраженному в миллисекундах (за дополнительными сведениями о функциях таймеров обращайтесь к разделу 17.5 на странице 487). Если работа потока прерывается посредством interrupt в момент выполнения им метода tick, метод slеер выбрасывает исключение типа InterruptedException. Управление передается из цикла for в предложение catch, где уведомление о прерывании потока посылается заново. Можно, разумеется, упомянуть исключение InterruptedExceptionв предложении throws объявления самого метода tick и просто позволить методу передать ответственность за обработку исключения вовне, но тогда ту же работу придется делать каждому методу, откуда tick вызывается. Повторение вызова прерывания в предложении catch позволяет методу tick при необходимости выполнить собственные завершающие операции и затем позволить остальному коду потока обработать событие прерывания надлежащим образом.

Вообще говоря, любой метод, выполняющий операции блокирования (непосредственно или косвенно), обязан предусмотреть возможность прерывания такой операции посредством метода interrupt и выбрасывания соответствующего исключения, если прерывание действительно произошло. Именно таким образом и действуют методы sleep и wait. В некоторых системах блокирование операций ввода-вывода приводит к выбрасыванию исключения типа InterruptedIOException, являющегося производным классом от базового IOException, объекты которого способны генерироваться большинством методов ввода-вывода (за подробными сведениями обращайтесь к главе 15). Даже если в ходе выполнения операции ввода-вывода система не в состоянии обеспечить реакцию на уведомление о прерывании, она может про верить наличие сигнала о событии прерывания перед началом операции и сгенерировать исключение. Cледовательно, прерываемый поток должен очистить состояние прерывания, если ему необходимо выполнить ввод-вывод как часть завершающего процесса. Однако  полагать, что interrupt: разблокирует поток, выполняющий операцию ввода вывода,  вообще говоря, нельзя.

Каждый метод любого класса выполняется как часть некоторого потока вычислений, но характеристики поведения метода, как правило, относятся к состоянию соответствующего объекта, но не к состоянию потока, вызвавшего метод. Раз так, резонно спросить, каким образом следует писать текст методов, чтобы позволить потокам реагировать на уведомления о прерывании и обеспечить их средствами управляемого завершения своей работы? Если метод способен блокировать объект, он должен и отвечать на сигналы о прерывании таким образом, как мы только что рассказали. В противном случае вы должны решить, что именно значит для конкретного метода уведомление о прерывании, и оговорить в контракте метода характеристики его поведения в подобных ситуациях. Впрочем, в большинстве случаев методам вообще нет нужды заботиться об обработке уведомлений о прерывании. Золотое правило состоит в том, что никогда не следует скрывать сигнал, посланный методом interrupt, явно очищая состояние прерывания либо отлавливая исключение InterruptedException и никак на него не реагируя, – в этом случае выполнение соответствующего потока прервать не удастся.

Механизм прерывания – это инструмент, позволяющий повысить эффективность "дружелюбного" и аккуратного многопоточного кода. Конечно, если код по своей сути агрессивен или злонамерен, тут уж не поможет ни механизм прерывания, ни какие бы то ни было иные средства организации вычислений.

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии