← Назад к вопросам

Как работает метод interrupt?

2.3 Middle🔥 111 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает метод interrupt в Java

Метод interrupt() — это механизм кооперативного прерывания потока в Java. Важно понимать, что interrupt не останавливает поток силой — он только устанавливает флаг прерывания, который поток должен сам проверить и обработать. Это безопасный способ запросить у потока завершение его работы.

Основной принцип работы

Каждый поток имеет флаг прерывания (interrupt status flag). Вызов interrupt() просто устанавливает этот флаг в true. Сам поток отвечает за проверку этого флага и вежливое завершение.

Поток A                  Поток B (рабочий)
  ↓                         ↓
thread.interrupt() → устанавливает флаг interrupt = true
                         ↓
                    Проверяет флаг
                         ↓
                    Завершается вежливо

Методы работы с прерыванием

public class InterruptExample {
    
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            while (true) {
                // Способ 1: проверить флаг
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("Поток получил сигнал прерывания");
                    break;
                }
                
                // Длительная работа
                System.out.println("Работаю...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // Способ 2: перехватить исключение
                    System.out.println("Прерван во время сна");
                    Thread.currentThread().interrupt(); // Восстановить флаг!
                    break;
                }
            }
            System.out.println("Поток завершен");
        });
        
        worker.start();
        Thread.sleep(3500);
        worker.interrupt(); // Запрос прерывания
        worker.join();
    }
}

Проблема: потеря флага при исключении

Это критически важный момент! Когда блокирующий метод (sleep, wait, join) получает прерывание, он:

  1. Очищает флаг (устанавливает в false)
  2. Бросает InterruptedException

Если не восстановить флаг, информация о прерывании теряется!

// ❌ НЕПРАВИЛЬНО: флаг потерян
while (!Thread.currentThread().isInterrupted()) {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        // Флаг очищен! Цикл не завершится
    }
}

// ✅ ПРАВИЛЬНО: восстанавливаем флаг
while (!Thread.currentThread().isInterrupted()) {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // Восстанавливаем
        break; // или выходим из цикла
    }
}

Три блокирующих метода которые реагируют на interrupt

1. Thread.sleep()

try {
    Thread.sleep(5000); // Может быть прерван
} catch (InterruptedException e) {
    System.out.println("Сон прерван");
    Thread.currentThread().interrupt();
}

2. Object.wait()

synchronized (lock) {
    try {
        lock.wait(5000); // Может быть прерван
    } catch (InterruptedException e) {
        System.out.println("Ожидание прервано");
        Thread.currentThread().interrupt();
    }
}

3. Thread.join()

try {
    thread.join(); // Может быть прерван
} catch (InterruptedException e) {
    System.out.println("Ожидание завершения потока прервано");
    Thread.currentThread().interrupt();
}

Проверка статуса прерывания

Два способа проверить флаг:

public class InterruptStatusCheck {
    
    public void method1() {
        // Способ 1: isInterrupted() — НЕ очищает флаг
        if (Thread.currentThread().isInterrupted()) {
            System.out.println("Флаг установлен");
        }
        // Флаг остаётся установленным
    }
    
    public void method2() {
        // Способ 2: interrupted() — ОЧИЩАЕТ флаг (статический метод)
        if (Thread.interrupted()) {
            System.out.println("Флаг был установлен, но теперь очищен");
        }
        // Флаг теперь false
    }
}

Практический пример: Graceful shutdown

public class WorkerThread extends Thread {
    private volatile boolean shouldStop = false;
    
    @Override
    public void run() {
        while (!shouldStop && !Thread.currentThread().isInterrupted()) {
            try {
                doWork();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // Поток был прерван
                System.out.println("Graceful shutdown initiated");
                Thread.currentThread().interrupt();
                break;
            }
        }
        cleanup();
    }
    
    private void doWork() {
        System.out.println("Работаю...");
    }
    
    private void cleanup() {
        System.out.println("Очистка ресурсов");
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        WorkerThread worker = new WorkerThread();
        worker.start();
        
        Thread.sleep(3500);
        worker.interrupt(); // Запрашиваем вежливое завершение
        worker.join();
        
        System.out.println("Поток завершен");
    }
}

Использование ExecutorService (рекомендуется)

Вместо явного управления потоками лучше использовать ExecutorService, который правильно обрабатывает interrupt:

public class ExecutorInterruptExample {
    
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        Future<?> future = executor.submit(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("Работаю");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        });
        
        Thread.sleep(3500);
        future.cancel(true); // Отправляет interrupt
        
        executor.shutdown();
        executor.awaitTermination(5, TimeUnit.SECONDS);
    }
}

Что NOT делать

// ❌ Никогда не делай так:
while (true) {
    // Ловишь исключение, но ничего не делаешь
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace(); // Это теряет информацию!
    }
}

// ❌ Старый способ (deprecated):
thread.stop(); // НИКОГДА! Может оставить синхронизацию нарушенной
thread.suspend(); // Deprecated, небезопасно

Итоговая таблица методов

МетодЭффектВажное
thread.interrupt()Устанавливает флагСам поток должен проверить
Thread.currentThread().isInterrupted()Проверяет флагНЕ очищает флаг
Thread.interrupted()Проверяет и очищаетСтатический метод
Блокирующие методыБросают InterruptedExceptionФлаг очищается!

Основное правило: interrupt — это вежливый запрос, а не насильственное приказание. Правильно обработанный interrupt позволяет потокам завершиться gracefully, освобождая ресурсы и сохраняя целостность данных.

Как работает метод interrupt? | PrepBro