In this lesson, we'll get acquainted with the Selector class. This class is in the java.nio.channels package, so you don't need to download or configure anything to use it. A Selector object can monitor one or more Channel objects, check their readiness to read/write, etc. And most importantly, a selector needs one stream, not one stream per channel.
We create selectors using the static open method:
Selector selector = Selector.open();
After that, channels can be registered in a selector object:
SelectionKey key1 = channel1.register(selector, SelectionKey.OP_READ); SelectionKey key2 = channel2.register(selector, SelectionKey.OP_WRITE);
The second parameter of the register method determines which operation the selector will monitor. If you need to monitor several operations at once, you can use bitwise OR:
SelectionKey.OP_READ | SelectionKey.OP_WRITE
When an I/O action occurs on any of the channels, the selector notifies us. This way you can, for example, read data from a large number of data sources.
Here we need to mention that a channel must be in non-blocking mode in order for it to be used with a selector:
channel1.configureBlocking(false); channel2.configureBlocking(false); SelectionKey key1 = channel1.register(selector, SelectionKey.OP_READ); SelectionKey key2 = channel2.register(selector, SelectionKey.OP_WRITE);
It follows that a selector will not work with a FileChannel, because a FileChannel cannot be switched to non-blocking mode (the configureBlocking method is declared in the SelectableChannel class, which FileChannel does not inherit).
From the diagram, you can see that selectors are suitable to use with sockets. We'll work with them at the end of the second module.
When registering a channel with a selector, we get a SelectionKey object. This object contains data about channel registration.
You can use the key to determine if the channel is ready for a certain value:
key.isReadable() key.isAcceptable() key.isConnectable() key.isWritable()
The key can give you the corresponding channel and selector:
Channel channel = key.channel(); Selector selector = key.selector();
You can attach any object to a key in order to track it in the future. This can be done either during channel registration (via the third argument) or later:
SelectionKey key = channel.register(selector, SelectionKey.OP_ACCEPT, object);
Later, you can get the attached object from the key:
Object object = key.attachment();
After registering channels with a selector, we can:
- find out the number of channels ready to perform specified operations
- block execution of our program until at least one channel is ready
- get a set of keys for ready channels
- and more
At the end of the second module, we will try out selectors in practice.