Показаны сообщения с ярлыком interrupt. Показать все сообщения
Показаны сообщения с ярлыком interrupt. Показать все сообщения

воскресенье, 17 марта 2013 г.

Прерывание потока. Для чего нужен Thread.currentThread().interrupt()

Часто можно встретить код, вроде такого:

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    //Восстанавливаем статус прерывания потока
    Thread.currentThread().interrupt();
}

Если не понимать логику работы прерываний, то напрашивается вопрос: для чего поток прерывает сам себя? На самом деле, выражение "прерывание потока" не очень удачное. Для этого есть устаревший(deprecated) и небезопасный метод stop, которым не рекомендуется пользоваться. Сам разработчик потока должен думать о корректном завершении работы своего потока.

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

Часто хорошим решением в этой ситуации является генерация исключения InterruptedException. Посмотрим, например, на поведение Thread.sleep() – приостанавливает поведение потока, но проверяет флаг, и как только замечает, что он установлен, бросает InterruptedException.

Но разве недостаточно в catch-секции просто завершить работу потока? Это не очень хорошее решение, так как обработка исключения может происходить где-то глубоко на низком уровне, а поток  должен совершить какую-нибудь работу перед завершением. А генерация исключения сбрасывает флаг, поэтому более высокоуровневые методы могут никогда не узнать о том, что поток был прерван, а следовательно и корректно завершить работу.

В следующем примере поток успешно поспит 2 раза, но третий сон будет прерван. Метод sleep2sec узнает об этом, но утаит эту информацию. Поэтому, поток будет выполнятся бесконечно, несмотря на проверку isInterrupted().

public class CatLife{
    
    final public static void main(String[]args) throws InterruptedException{
        Thread cat = new Thread(new Cat());
        cat.start();
        Thread.sleep(5000);
        cat.interrupt();
        cat.join();
    } 
    
    static class Cat implements Runnable{

        @Override
        public void run() {
            while(!Thread.currentThread().isInterrupted()){
                System.out.println("Сон начинается");
                sleep2sec();
                System.out.println("Сон закончился\n");
            }
            System.out.println("Завершение работы потока");
        }
        
        private void sleep2sec(){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println("Сон прерван");
            }
        }
        
    }

}

Тут есть 2 решения:
1. Пробросить исключение.
2. Снова установить флаг прерывания: Thread.currentThread().interrupt() в catch-секции, чтобы условие цикла выполнилось.