В этой лекции мы познакомимся с селектором, представленным классом Selector. Класс находится в пакете java.nio.channels, поэтому для его использования не понадобится ничего скачивать или настраивать. Объект типа Selector может следить за одним или несколькими объектами типа Channel, проверять их готовность к чтению/записи и т. д. И главное: для selector нужен один поток, а не по одному потоку на каждый канал.

Создаем селектора с помощью статического метода open:


Selector selector = Selector.open();

После чего в объекте селектора можно регистрировать каналы:


SelectionKey key1 = channel1.register(selector, SelectionKey.OP_READ);
SelectionKey key2 = channel2.register(selector, SelectionKey.OP_WRITE);

Второй параметр в метод register определяет, какую операцию будет мониторить селектор. Если нужно следить сразу за несколькими операциями, можно использовать побитовое ИЛИ:


SelectionKey.OP_READ | SelectionKey.OP_WRITE

Когда на любом из каналов будет происходить действие ввода-вывода, селектор уведомляет нас об этом. Таким образом можно, например, читать данные из большого количества источников данных.

Здесь нужно сделать примечание: чтобы канал можно было использовать с селектором, он должен находиться в так называемом неблокирующем режиме:


channel1.configureBlocking(false);
channel2.configureBlocking(false);
SelectionKey key1 = channel1.register(selector, SelectionKey.OP_READ);
SelectionKey key2 = channel2.register(selector, SelectionKey.OP_WRITE);

Из этого следует, что селектор не будет работать с FileChannel, так как FileChannel нельзя переключить в неблокирующий режим (метод configureBlocking объявлен в классе SelectableChannel, а FileChannel его не наследует).

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

SelectionKey

При регистрации канала с помощью селектора мы получаем объект SelectionKey. Этот объект содержит данные, представляющие регистрацию канала.

Из ключа можно определить, готов ли канал к определенному значению:


key.isReadable()
key.isAcceptable()
key.isConnectable()
key.isWritable()

Из ключа можно получить соответствующие канал и селектор:


Channel channel = key.channel();
Selector selector = key.selector();

К ключу можно прикрепить любой объект, чтобы в дальнейшем его отслеживать. Это можно сделать как во время регистрации канала (третьим аргументом), так и позже:

  1. SelectionKey key = channel.register(selector, SelectionKey.OP_ACCEPT, object);

  2. key.attach(object);

В дальнейшем из ключа можно получить прикрепленный объект:


Object object = key.attachment();

Заключение

После регистрации каналов с помощью селектора мы можем:

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

В конце второго модуля будем испытывать селекторы на практике.