«1. Обзор

В этой статье мы собираемся изучить основы одного из ключевых дополнительных API нового ввода-вывода (NIO2) в Java 7 — API асинхронного канала.

Это первая статья в серии статей, посвященных этой конкретной теме.

API-интерфейсы асинхронного канала являются усовершенствованием более ранних API-интерфейсов ввода-вывода (NIO), которые поставлялись с Java 1.4. Чтобы прочитать о NIO Selectors, перейдите по этой ссылке.

Еще одно усовершенствование API-интерфейсов NIO — это новый API-интерфейс файловой системы. Вы также можете узнать больше о его файловых операциях и операциях с путями на этом сайте.

Чтобы использовать асинхронные каналы NIO2 в наших проектах, мы должны импортировать пакет java.nio.channels, так как в нем собраны необходимые классы:

import java.nio.channels.*;

2. Как работают API асинхронных каналов

API асинхронных каналов были введены в существующий пакет java.nio.channels, проще говоря, путем префикса имен классов со словом Asynchronous.

Некоторые из основных классов включают: AsynchronousSocketChannel, AsynchronousServerSocketChannel и AsynchronousFileChannel.

Как вы могли заметить, эти классы похожи по стилю на стандартные API канала NIO.

И большинство операций API, доступных для классов каналов NIO, также доступны в новых асинхронных версиях. Основное отличие состоит в том, что новые каналы позволяют выполнять некоторые операции асинхронно.

Когда операция инициируется, API-интерфейсы асинхронного канала предоставляют нам две альтернативы для мониторинга и управления незавершенными операциями. Операция может вернуть объект java.util.concurrent.Future или мы можем передать ему java.nio.channels.CompletionHandler.

3. Подход будущего

Объект Future представляет собой результат асинхронного вычисления. Предполагая, что мы хотим создать сервер для прослушивания клиентских подключений, мы вызываем статический открытый API в канале AsynchronousServerSocketChannel и дополнительно привязываем возвращаемый канал сокета к адресу:

AsynchronousServerSocketChannel server 
  = AsynchronousServerSocketChannel.open().bind(null);

Мы передали значение null, чтобы система могла автоматически -назначить адрес. Затем мы вызываем метод accept для возвращенного сервера SocketChannel:

Future<AsynchronousSocketChannel> future = server.accept();

Когда мы вызываем метод accept для ServerSocketChannel в старом IO, он блокируется до тех пор, пока не будет получено входящее соединение от клиента. Но метод accept AsynchronousServerSocketChannel сразу же возвращает объект Future.

Универсальный тип объекта Future является возвращаемым типом операции. В нашем случае выше это AsynchronousSocketChannel, но он также может быть Integer или String, в зависимости от конечного типа возвращаемого значения операции.

Мы можем использовать объект Future для запроса состояния операции:

future.isDone();

Этот API возвращает true, если базовая операция уже завершена. Обратите внимание, что завершение в этом случае может означать обычное завершение, исключение или отмену.

Мы также можем явно проверить, была ли операция отменена:

future.isCancelled();

Он возвращает true только в том случае, если операция была отменена до нормального завершения, в противном случае он возвращает false. Отмена выполняется методом отмены:

future.cancel(true);

Вызов отменяет операцию, представленную объектом Future. Параметр указывает, что даже если операция началась, ее можно прервать. После завершения операции ее нельзя отменить

Чтобы получить результат вычисления, мы используем метод get:

AsynchronousSocketChannel client= future.get();

Если мы вызовем этот API до завершения операции, он будет заблокирован до завершения, а затем вернуть результат операции.

4. Подход CompletionHandler

Альтернативой использованию Future для обработки операций является механизм обратного вызова, использующий класс CompletionHandler. Асинхронные каналы позволяют указать обработчик завершения для использования результата операции:

AsynchronousServerSocketChannel listener
  = AsynchronousServerSocketChannel.open().bind(null);

listener.accept(
  attachment, new CompletionHandler<AsynchronousSocketChannel, Object>() {
    public void completed(
      AsynchronousSocketChannel client, Object attachment) {
          // do whatever with client
      }
    public void failed(Throwable exc, Object attachment) {
          // handle failure
      }
  });

API-интерфейс завершенного обратного вызова вызывается, когда операция ввода-вывода завершается успешно. Неудачный обратный вызов вызывается, если операция не удалась.

«Эти методы обратного вызова принимают другие параметры, чтобы позволить нам передавать любые данные, которые, по нашему мнению, могут быть подходящими для пометки вместе с операцией. Этот первый параметр доступен как второй параметр метода обратного вызова.

Наконец, очевидный сценарий — использование одного и того же CompletionHandler для разных асинхронных операций. В этом случае нам было бы полезно пометить каждую операцию, чтобы обеспечить контекст при обработке результатов, мы увидим это в действии в следующем разделе.

5. Заключение

В этой статье мы рассмотрели вводные аспекты API асинхронного канала Java NIO2.

Чтобы получить все фрагменты кода и полный исходный код для этой статьи, вы можете посетить проект GitHub.