Selector enables multiple I/O by providing the ability to select ready tasks for execution. Ready selection and multiple execution enable a single thread to efficiently manage multiple I/O channels simultaneously.

Two POSIX (Portable operating system interface) system calls, SELECT () and Poll (), have been available in C/C++ for many years. Many OSS offer similar functionality, but ready-to-select wasn’t a viable option for Java programmers until JDK 1.4.

Introduction to the

Access to theSocketChannelAfter that, it is wrapped directly into a task and submitted to the thread pool. After introducing a Selector, you need to register one or more selectable channels you created earlier with the Selector object. A key (SelectionKey) will be returned.SelectionKeyIt remembers the Channel you care about and tracks if the corresponding Channel is ready.

Each Channel has an operation of interest when it registers a Selector.

  • ServerSocketChannel registers only one operation on the selector, and its only operation of interest is ACCEPT, indicating that it only cares about the client’s connection requests
  • The number of socketchannels registered is usually multiple, because a server usually receives requests from multiple clients, so there are corresponding number of Socketchannels. The operations SocketChannel is interested in are CONNECT, READ, and WRITE because it also needs to READ and WRITE data to establish a connection with the server.

1 Selector

1.1 API

open

  • Open up a selector

The new selector is created by calling the openSelector method of the system’s default SelectorProvider object.

  • Notice the default selector provider
  • Mac JDK, so we need to download the corresponding platform under the JDK oh!

Selector.open()It’s not singleton, and every time you call that static method, it returns a new Selector instance.

2 SelectableChannel

Introduction to the

A channel that can be multiplexed by Selector.

In order to be used with a selector, an instance of the class must first pass through the register method. The method returns a new SelectionKey representing the registered object associated with the selection channel.

  • Register this channel with the given Selector and return the Selectionkey

Once registered with a Selector, until its channel remnant is unregistered. This includes unallocation of any resources allocated to the selected channel.

A channel cannot be unlogged directly. Instead, the key representing its registration must be unregistered. The cancel key requests channel selector logout during the next selection operation. One key can explicitly cancel by calling its cancel method. When a channel is closed, the keys of all channels are closed implicitly, either by calling their close method or by interrupting a thread that blocks the CHANNEL’s I/O operations. If the selector itself is closed, the channel is deregister and the key indicating its registration is invalidated without further delay.

Although a channel can be registered with more than one selector, it can only be registered once for each selector

Whether channel or not with one or more options may be called to determine whether to register an isRegistered method. Optional channels are safe to use by multiple concurrent threads.

Blocking mode

Selectable channels are either in blocking mode or non-blocking mode. In blocking mode, each I/O operation invoked on the channel blocks until it completes. I/O operations in non-blocking mode do not block and can transmit fewer bytes than are required or all may not have any bytes. The blocking mode of an optional channel can be invoked to determine the isBlocking method. A newly created optional channel is always in blocking mode. Non-blocking patterns are most useful in combination with select-based multiplexing. A channel must be placed into non-blocking mode with a selector prior to registration, and may not be returned to blocking mode until it has been unregistered.

A Selector is a component in Java NIO that can detect one or more NIO channels and know if the channel is ready for events such as read and write. Thus, a single thread can manage multiple channels and thus multiple network connections.

3 Why use Selector?

Selector allows a single thread to handle multiple channels. To use a Selector, you first register a Channel with the Selector, and then call its select(). This method blocks until an event is ready for a registered Channel. Once this method returns, the thread can process the events, such as new connections coming in, data receiving, and so on.

Advantage of single thread handling multiple channels: Fewer threads need to process channels. In fact, you can process all channels in a single thread. For an OS, context switching between threads is expensive and consumes system resources per thread. Therefore, the fewer threads used, the better.

But modern OSS and cpus are getting better at multitasking, and the overhead of multithreading is getting smaller and smaller. In fact, if the CPU is multi-core, not multitasking may be a waste of CPU performance. Being able to handle multiple channels using Selector is sufficient.

  • A single thread handles three channel sample diagrams using a Selector

4 Selector creation

Create a Selector by calling Selector. Open () as follows:

5 Register a Channel with Selector

To use a Channel with a Selector, a Channel must be registered with a Selector. Through SelectableChannel. The register () :

// Must be in non-blocking mode
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
Copy the code

configureBlocking

configureBlocking()Used to set the blocking mode for the channel, this method is calledimplConfigureBlocking implConfigureBlockingThe blocking mode is changed to the newly passed value, which defaults to true and passes false, and the channel is adjusted to non-blocking. The biggest advantage of NIO is that it is a non-blocking model, so it is usually set upSocketChannel.configureBlocking(false). You can do this by callingisBlocking()Determines the current mode of a socket channel.

When used with a Selector, a Channel must be in non-blocking mode, so you cannot use a FileChannel with a Selector because a FileChannel cannot switch to non-blocking mode. SocketChannel does.

Notice the second parameter to the register() method. This is a “set of events of interest,” meaning what events are interested in when listening to a Channel through Selector. Four different types of events can be listened on:

  • Read

A channel with data to read can be said to be “read ready”.

  • Write

A channel waiting for data to be written can be said to be “write ready”.

  • Connect

The channel raised an event meaning that the event is ready. Therefore, a channel that successfully connects to another server is said to be “connection-ready”.

  • Accept

A server socket channel that is ready to receive incoming connections is called “receive ready”.

The four kinds of events are represented by the four constants of SelectionKey:

If interested in more than one event, you can use “|” operator will constant connection:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
Copy the code

6 SelectionKey

SelectionKey

Encapsulates the registration relationship between a particular channel and a particular Selector.

SelectionKey object was SelectableChannel. Register (Selector sel, int ops) return and provide a tag says this registered relationship.

SelectionKeyContains two sets of bits (encoded as integers) : the channel operation that the registry is interested in and the channel-ready operation.

InterestOps (int) sets the interest of this key to a given value. You can call this method at any time. Whether it blocks and for how long depends on the implementation.

  • The interest set determines which action classes are tested for readiness the next time one of the selector’s selection methods is called. Initialize the set of interest with the value given when the key was created; You can change it later via interestOps(int).
  • The ready set identifies the type of action for which the key’s selector has detected that the key’s channel is ready. Initialize the ready set to zero when creating the key; Otherwise, initialize it to zero. It may be updated later by the selector during the selection operation, but not directly.

When you register a Channel with a Selector, the register() method returns a SelectionKey object containing some of the properties you’re interested in:

  • Ready set
  • Channel
  • Selector
  • Attached object (optional)

Collection of interest

Collection of interest

An interest collection is a collection of events that you choose to be interested in. Interest collections can be read and written via SelectionKey, like this:

int interestSet = selectionKey.interestOps();
booleanIsInterestedInAccept = (interestSet & selectionkey.op_accept) == selectionkey.op_accept;boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead    = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite   = interestSet & SelectionKey.OP_WRITE;
Copy the code

The “bit and” interest set and the SelectionKey constant determine whether an event is in the interest set.

Ready set

A collection of operations for which the channel is ready. After a Selection, you first access the Ready set. The ready collection can be accessed like this:

You can detect what events or actions are ready in a channel in the same way you detect an interest collection. You can also use the following four methods, all of which return a Boolean type:

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

Channel + Selector

Accessing channels and selectors from the SelectionKey is easy. As follows:

Attached object

An object or more information can be attached to SelectionKe to make it easy to identify the channel. For example, you can attach a Buffer that is used with a channel, or an object that contains aggregated data. The usage method is as follows:

selectionKey.attach(theObject);
Object attachedObj = selectionKey.attachment();
Copy the code
  • Appends the given object to the key.

An attached object may be acquired later by the attachment. Only one object can be attached at a time; Calling this method causes any previous attachments to be discarded. The current attachment can be discarded by attaching null.

You can also attach objects when registering a Channel with a Selector with a register(). Such as:

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

Select channels by Selector

Once one or more channels have been registered with Selector, the overloaded select() can be called. These methods return channels that are ready for the event you are interested in (such as connect, receive, read, or write). That is, if you are interested in read-ready channels, the select() method returns those channels for which the read event is ready.

select() API

select()

Blocks until at least one channel is ready on the event you registered

Gracefully close the thread executing select()

  • Use a volatile Boolean variable to indicate whether a thread has stopped
  • To stop a thread, call interrupt() to stop the thread because the thread may be in wait() or sleep() to improve the timeliness of stopping the thread
  • If you are processing blocking IO, use InterruptibleChannel instead of blocking IO. For NIO, if the thread is blocked by select() and the condition variable cannot be detected in time, wakeup() needs to be called manually to wakeup the thread so that it can detect the condition variable.

select(long timeout)

The same as select(), except that it will block for a maximum of timeout milliseconds.

selectNow()

Does not block and returns immediately whatever channel is ready. (This method performs a non-blocking selection operation. If no channel has been selected since the last selection, this method returns 0).

The int returned by the select() series of methods indicates how many channels are ready, that is, how many channels have become ready since the last call to select(). If you call select(), it returns 1 because one channel is ready, and if you call select() again, it returns 1 if the other channel is ready. If nothing is done to the first ready channel, there are now two ready channels. But only one channel is ready between each select() method call.

selectedKeys()

Once the select() method is called and the return value indicates that one or more channels are ready, the ready channels in the selected key set can be accessed by calling selector’s selectedKeys() method:

Set selectedKeys = selector.selectedKeys();
Copy the code

When registering a Channel like a Selector, the channel.register () method returns a SelectionKey object. This object represents the Channel registered with that Selector. These objects are accessed through the selectedKeySet() method of SelectionKey.

The selectedKeys can be traversed to access ready channels:

	Set selectedKeys = selector.selectedKeys();
	Iterator keyIterator = selectedKeys.iterator();
	while(keyIterator.hasNext()) {
	    SelectionKey key = keyIterator.next();
	    if(key.isAcceptable()) { 
	    } 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 through each key in the selected key set and detects ready events for channels corresponding to each key.

Note that keyiterator.remove () is called at the end of each iteration. The Selector doesn’t remove the SelectionKey instance from the selectedKeys itself. It must be removed by itself when the channel is processed. The next time the channel becomes ready, the Selector puts it in the selectedKeys again.

The channel returned by the selectionkey.channel () method needs to be converted to the type you want to work with, such as ServerSocketChannel or SocketChannel.

wakeUp()

If a thread blocks after calling select(), there is a way to make it return from select() even if no channel is ready. Simply call Selector. Wakeup () from another thread on the object where the first thread called select(). Threads that block on select() will return immediately.

If another thread calls wakeup() and no thread is currently blocking select(), the next thread to call select() will immediately “wakeup”.

close()

Calling close() after a Selector is used closes that Selector and invalidates any SelectionKey instances registered with that Selector. But the channel itself does not close.

The sample

Open a Selector, register a channel with that Selector, and continuously monitor the readiness of the Selector’s four events (accept, connect, read, and 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 selectedKeys = selector.selectedKeys();
		Iterator 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