Ответ
Неблокирующий ввод-вывод (NIO) — это модель, при которой поток, инициировавший операцию чтения/записи, не блокируется в ожидании ее завершения. Если данные для чтения недоступны или буфер для записи заполнен, операция немедленно возвращает управление, позволяя потоку выполнять другую работу.
Ключевые компоненты Java NIO:
Channel— абстракция для двусторонней связи (чтение/запись). Основные реализации:SocketChannel,ServerSocketChannel,FileChannel.Buffer— контейнер для данных, с которым работают каналы.Selector— объект, который мониторит несколько каналов на предмет готовности к операциям ввода-вывода.
Принцип работы (мультиплексирование):
- Каналы переводятся в неблокирующий режим:
channel.configureBlocking(false). - Каналы регистрируются в
Selectorс указанием интересующих событий (OP_READ,OP_WRITE,OP_ACCEPT,OP_CONNECT). - Поток вызывает
selector.select(), который блокируется до тех пор, пока хотя бы один из зарегистрированных каналов не будет готов. - Получив набор готовых каналов (
selectedKeys), поток обрабатывает их по очереди.
Пример сервера:
try (ServerSocketChannel serverChannel = ServerSocketChannel.open();
Selector selector = Selector.open()) {
serverChannel.bind(new InetSocketAddress(8080));
serverChannel.configureBlocking(false); // Неблокирующий режим
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // Интересует событие подключения
while (true) {
selector.select(); // Ожидание событий
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iter = readyKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove();
if (key.isAcceptable()) {
// Принято новое подключение
SocketChannel client = serverChannel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
// Канал готов к чтению данных
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
client.read(buffer); // Не блокируется
// ... обработка данных
}
}
}
}
Преимущество: Один поток может эффективно обслуживать сотни или тысячи сетевых соединений, в отличие от блокирующего IO, где на каждое соединение обычно требуется отдельный поток.