Сначала некоторое объяснение:
Прерванный статус потока — это, по сути, логический флаг, который устанавливается в значение «истина» с помощью interrupt()
. Текущее состояние этого флага можно прочитать с помощью Thread.currentThread().isInterrupted()
.
Если прерываемая операция (например, Object.wait()
или Thread.sleep()
) находит установленный флаг прерывания, она выдает InterruptedException
и в то же время очищает (устанавливает значение "false") флаг, который может выглядеть следующим образом:
if ( Thread.interrupted() ) { throw new InterruptedException(); }
Обратите внимание и запомните, что Thread.interrupted()
неявно очищает флаг прерывания! Это означает, что к моменту выполнения вашего catch( InterruptedException ie) {...}
сам поток уже не знает, что он был прерван.
Тем не менее, давайте посмотрим на два примера:
Сначала пример задачи, которая поддерживает отмену. Здесь нам все равно, как далеко продвинется задача до того, как будет прервана:
public void run() {
int x = 0;
try {
while (x < 10) {
Thread.sleep(1000); // Some interruptible operation
x++;
}
System.out.println("x = " + x);
} catch (InterruptedException ie) {
System.out.println("Interrupted: x = " + x);
// We know we've been interrupted.
// Let the caller know it, too:
Thread.currentThread().interrupt();
}
}
Этот код пытается посчитать x от 0 до 10. Если его не прервать, он завершится и выведет «x = 10». Однако, если поток прерывается между ними, будет выброшено InterruptedException
, прервав текущую задачу увеличения x. В этом случае вывод может быть любым, от «Прервано: x = 0» до «Прервано: x = 9», в зависимости от того, когда поток был прерван.
Обратите внимание, что считается хорошей практикой восстанавливать флаг прерывания потока перед выходом, поскольку в противном случае вызывающая сторона этого метода run()
не увидит статус прерывания.
Теперь, если важно, чтобы наша задача выполнялась полностью, чтобы вывод всегда был «x = 10», что означает, что задача не поддерживает отмену, нам нужен другой подход:
public void run() {
int x = 0;
boolean wasInterrupted = false; // <- This is the local variable to store the interruption status
while (x < 10) {
wasInterrupted = wasInterrupted || Thread.interrupted(); // not really needed in this case, but for the sake of completeness...
try {
Thread.sleep(1000); // <- Some interruptible operation
} catch (InterruptedException e) {
wasInterrupted = true;
}
x++;
}
System.out.println("x = " + x);
if ( wasInterrupted ) {
Thread.currentThread().interrupt();
}
}
В этом случае мы продолжаем обработку даже после InterruptedException
, пока задача не будет завершена. Чтобы оставаться вежливым, если мы обнаруживаем прерывание, мы сохраняем это условие в wasInterrupted
, чтобы мы могли правильно установить флаг прерывания перед возвратом из метода.
Вот что имеется в виду
должен сохранять статус прерывания локально и восстанавливать его непосредственно перед возвратом.
Он говорит «должен», потому что мы строго не обязаны обрабатывать прерывания таким образом — мы можем просто игнорировать любые InterruptedException
и просто завершить нашу задачу, а затем вернуться. Однако это не рекомендуемая практика, упомянутая выше, и в некоторых сценариях может вызвать проблемы.
person
JimmyB
schedule
27.10.2011