Что происходит с потоком (thread) в Java при выполнении блокирующей I/O операции?

«Что происходит с потоком (thread) в Java при выполнении блокирующей I/O операции?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Поток переходит в состояние BLOCKED и освобождает процессорное время (CPU). Он будет находиться в этом состоянии до тех пор, пока операция ввода-вывода не завершится на уровне операционной системы (например, данные не будут прочитаны с диска или из сети).

Ключевые моменты:

  • Блокировка: Поток «засыпает», ожидая данных. Это неэффективно с точки зрения использования ресурсов, особенно при большом количестве одновременных соединений.
  • Освобождение CPU: Планировщик потоков JVM может выполнять другие готовые к работе (RUNNABLE) потоки.

Пример блокирующего чтения из файла:

try (FileInputStream fis = new FileInputStream("data.txt")) {
    int byteData;
    // Поток блокируется на каждом вызове read(), пока байт не будет доступен
    while ((byteData = fis.read()) != -1) {
        System.out.print((char) byteData);
    }
} catch (IOException e) {
    e.printStackTrace();
}

Альтернативы для избежания блокировки:

  1. Неблокирующий I/O (NIO) с Selector: Поток может опрашивать множество каналов на готовность, не блокируясь на каждом.
  2. Асинхронный I/O (NIO.2): Операция инициируется и завершается в фоновом режиме, уведомляя поток через callback или Future.
  3. Использование пула потоков: Блокирующие операции делегируются отдельным потокам из пула, чтобы основной поток (например, в веб-сервере) мог продолжать принимать новые запросы.