Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает метод 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) получает прерывание, он:
- Очищает флаг (устанавливает в false)
- Бросает 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, освобождая ресурсы и сохраняя целостность данных.