Java 21 принес Virtual Threads (Preview), и всё чаще появляется соблазн запускать их через
ExecutorService. Но вот что важно помнить:📌
shutdownNow() — опасная ловушка при работе с виртуальными потоками.
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
Future<?> future = executor.submit(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("Interrupted!");
}
});
executor.shutdownNow(); // ❗️Ничего не произойдёт
💡 Почему?
Метод
shutdownNow() не может прервать виртуальные потоки, если они запущены через Executors.newVirtualThreadPerTaskExecutor(). Он лишь помечает пул как завершённый и возвращает список задач, которые ещё не стартовали.Уже запущенные виртуальные потоки продолжают выполняться —
Thread.interrupt() не работает, потому что у виртуальных потоков отсутствует связь с ThreadGroup, к которому привязан shutdownNow.⚠️ Следствие:
Если вы рассчитываете, что
shutdownNow() "остановит всё" — вы можете получить утечку задач или зависания.🔧 Что делать?
1. Контролируйте завершение через
Future.cancel(true) — он вызывает interrupt() на конкретной задаче.2. Стройте явную кооперативную модель отмены — с флагами или
Thread.interrupted() внутри задачи.3. Для массовой отмены — храните
Future задач и отменяйте вручную.👉@BookJava
