A selector is a Java NIO component that can examine one or more NIO channels and determine which ones are ready to read or write. Such a single thread can manage multiple channels and thus multiple network connections. ### Why use Selector The advantage of using a single thread to handle multiple channels is that you need fewer threads to handle channels. In fact, you can process all your channels with just one thread. Switching between threads is expensive for the operating system, and each thread also occupies some resources (memory) in the operating system. Therefore, the fewer threads you use, the better.

But keep in mind that modern operating systems and cpus are getting better at multitasking, so the overhead of multithreading gets smaller over time. In fact, if a CPU has multiple cores, you may be wasting CPU resources by not multitasking. In any case, this design discussion belongs in a different text. Suffice it to say here, you can use a Selector to handle multiple channels for a single thread.

Here is an example of a thread handling three channels using Selector:

Selector selector = Selector.open();
Copy the code

To use a channel with a Selector, you must register a channel with a Selector. This is the use of SelectableChannel. Register () method, as shown below:

channel.configureBlocking(false); SelectionKey = channel.register(selector, selectionkey.op_read);Copy the code

Channels must be in non-blocking mode to be used with selectors. This means you can’t use FileChannel and Selector, because FileChannel can’t switch to non-blocking mode. The socket channel will work properly.

Note the second parameter, which is set by type, meaning that the event type you set to listen to a Channel, through the selector, has four blocked events:

  1. Connect
  2. Accept
  3. Read
  4. A channel that writes an “event” is also considered a “ready” event. Therefore, the channel that successfully connects to another server is Connect Connect ready. The server socket channel that accepts incoming connections is “Accept” ready. The channel ready to Read data Read is ready. A channel ready to Write data is ready to Write. These four events are represented by four SelectionKey constants:
  5. SelectionKey.OP_CONNECT
  6. SelectionKey.OP_ACCEPT
  7. SelectionKey.OP_READ
  8. Selectionkey. OP_WRITE If you need to set multiple events, or put constants together, like this: selectionKey. OP_WRITE
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;    
Copy the code

As you saw in the previous section, when you register a Channel with a selector, the register () method returns a SelectionKey object. The SelectionKey object contains some interesting properties:

  • The interest set
  • The ready set
  • The Channel
  • The Selector
  • #####Interest Set The Interest Set is The Set of events that you are interested in in “select”, as described in The section “Register channels with selectors”. You can read and write interest sets like the following via SelectionKey:
int interestSet = selectionKey.interestOps();

boolean isInterestedInAccept  = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead    = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite   = interestSet & SelectionKey.OP_WRITE;    
Copy the code

As you can see, you can find out if a particular event is for the collection with a given SelectionKey constant and interest group. #####Ready Set A Ready Set is a Set of operations for preparing a channel. You will access the Ready Set primarily after selection. Choose to explain in a later chapter. You can access readySet like this:

int readySet = selectionKey.readyOps();
Copy the code

You can test channel-ready events/actions in the same way as interest sets. However, you can also use these four methods, all of which reconstruct a Boolean value:

selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();
Copy the code

#####Channel + Selector Accessing the Channel + Selector from the SelectionKey is pretty neat. How this is done:

Channel  channel  = selectionKey.channel();

Selector selector = selectionKey.selector();    
Copy the code

#####Attaching Objects you can attach to SelectionKey is a convenient way to recognize a given channel channel or attach more information to a channel channel. For example, you can connect the buffer you are using to a channel or an object that contains more aggregate data. Here’s how you attach objects:

selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();
Copy the code

In the register () method, you can also append an object when registering a Channel with a Selector. Here’s how it looks like this:

SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
Copy the code

##### selects channels by Selector Once you have registered one or more channels with a Selector, you can call one of the select () methods. These methods return a “ready” channel for the event (connect, receive, read, or write) that you are interested in. In other words, if you are interested in the channel you are ready to read, you will receive the channel ready to read from the select () method. Here is the select() method:

  • int select()
  • int select(long timeout)
  • Int selectNow() int select() until at least one channel is ready to register the event. Int SELECT (long timeout) : same as select(), except its maximum timeout milliseconds (parameter). Int selectNow() : It returns immediately with whatever channel is ready. The int returned by the select () method tells you how many channels are ready. That is, how many channels have been prepared since the last call to select (). If you call select () and return 1 because one channel is ready and you call select () again and another channel is ready, it will return 1 again. If the first channel you have ready doesn’t do anything, you now have 2 ready channels, but only one channel is ready between each select () call. #####selectedKeys() Once you have called a select () method and its return value has indicated that one or more channels are ready, You can access Ready Channels through “selected key set” by calling the selectedKeys () method. Here’s how it looks like this:
Set<SelectionKey> selectedKeys = selector.selectedKeys();    
Copy the code

When you register a Channel with Selector, the channel.register () method returns the SelectionKey object. This key indicates that the channel is registered with the selector. These keys can be accessed through the selectedKeySet () method. From SelectionKey. You can iterate over this selected key set to access the ready channel. Here’s how it looks like this:

Set<SelectionKey> selectedKeys = selector.selectedKeys();

Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

while(keyIterator.hasNext()) {
    
    SelectionKey key = keyIterator.next();

    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.

    } else if (key.isConnectable()) {
        // a connection was established with a remote server.

    } else if (key.isReadable()) {
        // a channel is ready for reading

    } else if (key.isWritable()) {
        // a channel is ready for writing
    }

    keyIterator.remove();
}
Copy the code

This loop iterates over the keys in the selected key set. For each SelectionKey, it tests the SelectionKey to determine that the channel referenced by the SelectionKey is ready.

Note the keyiterator.remove () call at the end of each iteration. The selector does not remove the SelectionKey instance from the selected SelectionKey set itself. When you are finished with channel processing, you must perform this operation. The next time the channel becomes Ready, the selector adds it again to the selected key.

The channel returned by the selectionkey.channel () method should be converted to the channel you need to use, such as ServerSocketChannel or SocketChannel.

###wakeUp() a thread that calls the blocked SELECT () method can leave the select () method even if no channel is not ready. This is done by having different threads call Selector.wakeup () on Selector, with the first thread calling select (). The threads waiting in select () will return immediately.

If a different thread calls wakeup () and no threads are currently blocking inside select (), the next thread calling select () will immediately “wakeup”. When you’re done with Selector, you call the close() method. This closes the selector and invalidates all SelectionKey instances registered in the selector. The channel itself is not closed. This is a complete example that opens a Selector, registers a channel (the channel instance is omitted), and continuously monitors whether the Selector is ready for four events (accept, connect, read, write).

Selector selector = Selector.open();

channel.configureBlocking(false);

SelectionKey key = channel.register(selector, SelectionKey.OP_READ);


while(true) {

  int readyChannels = selector.select();

  if(readyChannels == 0) continue;


  Set<SelectionKey> selectedKeys = selector.selectedKeys();

  Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

  while(keyIterator.hasNext()) {

    SelectionKey key = keyIterator.next();

    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.

    } else if (key.isConnectable()) {
        // a connection was established with a remote server.

    } else if (key.isReadable()) {
        // a channel is ready for reading

    } else if (key.isWritable()) {
        // a channel is ready forwriting } keyIterator.remove(); }}Copy the code