public abstract void wakeup();
Selector (Selector) is a Java NIO system that can detect one or more NIO channels and determine whether to prepare for read/write events. In this way, a single thread can manage multiple channels to manage multiple network connections. The advantage of using a single thread to process multiple Channels is that fewer threads are needed to process Channels. In fact, you can use only one thread to process all the channels. For the operating system, context switching between threads is costly, and each thread occupies some system resources (such as memory ). Therefore, the fewer threads used, the better.
When using selectors, there are three main classes involved:
- Selector ):The selector class manages information about a registered channel set and their readiness. The channel and Selector are registered together, And the selector is used to update the channel readiness status.
- SelectableChannel ):It is the parent class of all channel classes that support readiness check. The FileChannel object is not selectable because it does not inherit SelectableChannel. All socket channels are selectable, including channels obtained from pipeline (Pipe) objects. Only SelectableChannel can be registered on the Selector object. A channel can be registered on multiple selectors, but each selector can only be registered once.
- SelectionKey ):The selection key encapsulates the registration relationship between a specific channel and a specific selector. The selection key object is returned by SelectableChannel. register () and a mark indicating the registration relationship is provided. The selection key contains two bit sets (encoded in integer form), indicating the channel operations that the registration relationship cares about and the operations that the channel has prepared.
Let's take a look at the source code of the SelectableChannel class as follows:
public abstract class SelectableChannel extends AbstractChannel implements Channel { public abstract SelectionKey register(Selector sel, int ops) throws ClosedChannelException; public abstract SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException; public abstract boolean isRegistered(); public abstract SelectionKey keyFor(Selector sel); public abstract int validOps(); public abstract void configureBlocking(boolean block) throws IOException; public abstract boolean isBlocking(); public abstract Object blockingLock();}
Calling the register () method registers a non-blocking channel to a selector. If you try to register a blocked channel, register () throws an unchecked IllegalBlockingModeException. In addition, once the channel is registered, it cannot return to the blocking status. In this case, the IllegalBlockingModeException will be thrown when the configureBlocking () method is called. Besides, if you try to register a disabled SelectableChannel instance, The ClosedChannelException will also be thrown, as indicated by the method prototype. Before a channel is registered to a selector, it must be set to non-blocking mode (by calling configureBlocking (false )). As follows:
Channel. configureBlocking (false); // set to non-blocking mode SelectionKey key = channel. register (selector, Selectionkey. OP_READ); // CALL THE METHOD
When used with Selector, the Channel must be in non-blocking mode. This means that FileChannel and Selector cannot be used together, because FileChannel cannot be switched to non-blocking mode. All socket channels are supported.
The second parameter of the register () method can be specified to listen to four different types of events. These four events are represented by four constants of SelectionKey, as shown below:
public static final int OP_READ; public static final int OP_WRITE; public static final int OP_CONNECT; public static final int OP_ACCEPT;
The specific explanation is as follows:
The channel triggers an event, which means the event is ready. If you are interested in more than one event, you can use the bitwise OR operator to connect constants as follows:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
The following describes the methods defined in the SelectionKey abstract class:
Public abstract class SelectionKey {public abstract SelectableChannel channel (); // obtain the Channel object public abstract Selector selector (); // obtain the Selector object public abstract void cancel (); public abstract boolean isValid ();}
A key represents the registration relationship between a specific channel object and a specific selector object. The first two methods reflect this relationship: the channel () method returns the SelectableChannel object related to the key, and the selector () method returns the relevant Selector object.
A key object represents a specific registration relationship. When this relationship should be terminated, you can call the cancel () method of the SelectionKey object. You can call the isValid () method to check whether it still represents a valid relationship. When the key is canceled, it is placed in the set of canceled keys of the related selector. Registration will not be canceled immediately, but the key will expire immediately.
When the channel is closed, all related keys are automatically canceled (Remember, a channel can be registered on multiple selectors ). When the selector is disabled, all channels registered with the selector will be logged out, and the related keys will be immediately invalidated (canceled ). Once the key is invalid, A CancelledKeyException is thrown when you call the selection-related method.
public abstract int interestOps(); public abstract void interestOps(int ops); public abstract int readyOps();
A SelectionKey object contains two bit masks encoded in integer form: an operation (instrest set) that indicates the actions concerned by the channel/selector combination ), the other indicates that the channel is ready for the operation to be executed (ready Set ).
The current interest set can be obtained by calling the interestOps () method of the key object. Initially, this should be the value passed in when the channel is registered. This interset set is never changed by the selector, but you can change it by calling the interestOps () method and passing in a new bit mask parameter.
The interest set can also be changed by registering the channel to the selector (actually calling interestOps () in a roundabout way ()). When the select () operation on the relevant Selector is changing the key's interest set, the ongoing selection operation will not be affected. All changes will be reflected in the next call of select. The interest collection is the collection of events you are interested in. You can use the interestOps () method in SelectionKey to read and write the interest set, as shown below:
int interestSet = selectionKey.interestOps();boolean isInterestedInAccept = (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;
We can see that using bitwise AND to operate the interest set and the given SelectionKey constant can determine whether a certain event is in the interest set.
You can call the readyOps () method of the key to obtain the ready operations of the related channel. The ready set is a subset of the interest set and represents the operations that have been completed since the previous select () call in the interest set. The ready set is a set of operations that are ready for the channel. After a Selection, you will first call the readyOps () method to access the ready Set:
int readySet = selectionKey.readyOps();
As mentioned earlier, there are four channel operations that can be used to test readiness. You can check the status by testing the bit mask as in the code above, but the SelectionKey class defines four easy-to-use Boolean methods to test these bit values for you. Each method has the same effect as using a specific mask to test the readyOps () method:
public final boolean isReadable(); public final boolean isWritable(); public final boolean isConnectable(); public final boolean isAcceptable();
If (key. isWritable () is equivalent to: if (key. readyOps () & SelectionKey. OP_WRITE )! = 0)
These four methods can be safely called on any SelectionKey object. You cannot register an operation that is not supported by a channel. This operation will never appear in the ready collection. When an unsupported operation is called, false is always returned, because this operation is never ready for this channel.
The readiness status indicator returned by the readyOps () method of the selection key is only a prompt, not a guarantee. Underlying channels are constantly changing at any time. Other threads may execute operations on the channel and affect its readiness. At the same time, the features of the operating system always need to be considered.
public final Object attach(Object ob);public final Object attachment();
These two methods allow you to place an attachment on the key and obtain it later. This is a convenient way to associate any object with a key. This object can reference any objects that are meaningful to you, such as business objects, session handles, and other channels. This allows you to traverse the selector-related keys and use the attached object handle as a reference to obtain the relevant context.
The attach () method saves the reference of the provided object in the key object. The SelectionKey class does not use it for any other purposes except for saving it. Any attachment reference that was previously stored in the key will be replaced. You can use null values to clear attachments. You can call attachment () to obtain the attachment handle associated with the key.
If the selection key lasts for a long time, but the object you attached should not exist for that long, remember to clear the attachment after completion. Otherwise, the object you attach will not be reclaimed by garbage collection, and you will face memory leakage.
Recall the register () method of SelectableChannel. An overload method that accepts an Object parameter is as follows:
SelectionKey key = channel. register (selector, SelectionKey. OP_READ, myObject); equivalent to: SelectionKey key = channel. register (selector, SelectionKey. OP_READ); key. attach (myObject );
The last thing to note about SelectionKey is concurrency. In general, the SelectionKey object is thread-safe, but it is very important to know that the operation to modify the interest set is synchronized through the Selector object. This may cause calls to the interestOps () method to block a period of uncertain time. The lock policies used by the selector (for example, whether to keep these locks throughout the selection process) depend on the specific implementation. Fortunately, this multi-processing capability is specially designed to manage multiple channels using a single thread. The Selector Used by multiple threads can only cause problems when the system is particularly complex.
The selector maintains the set of registered channels, and any of these registration relationships is encapsulated in the SelectionKey object.Each Selector object maintains a set of three keys:
public abstract class Selectory { // This is a partial API listing public abstract Set keys(); public abstract Set selectedKeys(); public abstract int select() throws IOException; public abstract int select(long timeout) throws IOException; public abstract int selectNow() throws IOException; public abstract void wakeup();}
- A set of Registered keys (Registered key set)A set of registered keys associated with the selector. Not all registered keys are valid. This set is returned using the keys () method and may be empty. The set of registered keys cannot be directly modified. If you try to do so, java. lang. UnsupportedOperationException will be introduced.
- Selected key set)A subset of a set of registered keys. Each member of this set is determined by the related channel selector (in the previous selection operation) as prepared and included in the operations in the key's interest set. This set is returned through the selectedKeys () method (and may be empty ).
Do not mix the set of selected keys with the ready Set. This is a set of keys. Each key is associated with a channel that has at least one operation ready. Each key has an embedded ready Set that indicates the operations that have been prepared for the associated channel.
The key can be removed from this set (the set of selected keys ).But cannot be added. An attempt to add an element to the selected key set throws java. lang. UnsupportedOperationException.
- Canceled key set)A subset of the registered key set. This set contains the key called by the cancel () method (SelectionKey class method) (this key has been invalidated ), however, they have not been deregistered. This set is a private member of the selector object and thus cannot be accessed directly. In a newly initialized Selector object, all the three sets mentioned above are empty.
◇ Selection process
The core of the Selector class is the selection process. Basically, a selector is a package for native call, such as select () and poll (), or for a specific system call of a similar operating system. However, Selector does not simply send parameters to local code. It applies a specific process to each selection operation. The understanding of this process is the basis for rational management of keys and state information they represent.
The select () method of the Selector class has three different forms:
- Int select ()This call will be infinitely blocked when no channel is ready. Once at least one registered channel is ready, the selector selection key is updated and the ready Set of each ready channel is updated. The returned value is the number of identified channels. Under normal circumstances, these methods will return a non-zero value, because it will block until a channel is ready.
- Int select (long timeout)This call is exactly the same as in the previous example, except if no channel is ready within the timeout period you provided (in milliseconds), it returns 0. If one or more channels are ready before the time limit ends, the key status will be updated and the method will return immediately at that time. If the timeout parameter is set to 0, it will wait for an indefinite period, so it is equivalent to using the select () without parameters in all aspects.
- Int selectNow ()This form is completely non-blocking. The selectNow () method executes the readiness check process, but does not block it. If no channel is ready, it returns 0 immediately.
The select Operation is executed by the selector when any of the three forms of select () is called.The following steps are executed for any form of call:
1.The set of canceled keys will be checked. If it is not empty, the keys in each canceled key set will be removed from the other two sets, and the related channels will be logged out. After this step is completed, the set of canceled keys will be empty.
2.The interest set of keys in the set of registered keys will be checked. After the check in this step is executed, changes to the interest set will not affect the remaining check process.
Once the readiness condition is set, the underlying operating system queries to determine the true readiness status of the operations that each channel cares about. Depending on the specific select () method call, if no channel is ready, the thread may be blocked at this time, usually there is a timeout value. For those channels that are not ready, no operations will be performed. For channels that have at least one operation in the interest set indicated by the operating system, either of the following operations is performed:
- If the channel key is not in the selected key set, the ready Set of the key is cleared, then, it indicates that the bit mask of the operations that have been prepared for the current channel discovered by the operating system will be set.
- Otherwise, the key is in the selected key set. The ready set of keys indicates the bit mask update of the currently prepared operations discovered by the operating system. All previous operations that are no longer in the ready state will not be cleared. In fact, all bits will not be cleared. The ready Set determined by the operating system is separated from the previous ready Set by bit. Once the key is placed in the set of selected keys of the selector,Its ready set will be cumulative. BITs are only set and not cleared. Assume that the previous ready Set is 100, and the 010 operation is ready. At this time, the ready Set is 110, instead of 010. This is accumulation and will not be cleared.
3.Step 2 may take a long time, especially when the stimulated thread is in sleep state. Keys related to this selector may be canceled at the same time. When Step 2 ends, step 1 will be re-executed to complete the cancellation of any channel that has been canceled during the selection process.
4.The value returned by the select operation is the ready set in step 2.
Modified keyInstead of the total number of channels in the set of selected keys. The returned value is not the total number of prepared channels, but the number of channels that enter the ready state after the previous select () call. The channels that are ready in the previous call and are still ready in this call will not be included, and those that are ready in the previous call but are no longer in the ready status will not be included. These channels may still exist in the selected key set, but will not be included in the return value. The returned value may be 0.
◇ Stop the selection process
The last method in the Selector API,
Wakeup () provides the ability to enable the thread to exit from the blocked select () method:
public abstract void wakeup();
There are three ways to wake up the thread sleeping in the select () method:
- Call wakeup ()Calling the wakeup () method of the Selector object will immediately return the first selection operation on the Selector that has not yet been returned.
- Call close ()If the close () method of the selector is called, any thread that is blocked in the selection operation will be awakened, just as the wakeup () method is called. The selector-related channel will be canceled, and the key will be canceled.
- Call interrupt ()If the interrupt () method of the thread in sleep is called, its return status is set. If the wake-up thread tries to perform the I/O operation on the channel, the channel will immediately close and the thread will catch an exception. The wakeup () method will elegantly wake up a thread sleeping in the select () method. If you want a sleep Thread to continue running after it is directly interrupted, You need to perform some steps to clear the interrupt status (see the relevant documentation of Thread. interrupted ).
The Selector object captures an InterruptedException exception and calls the wakeup () method. Note that none of these methods will close any related channel. Interrupting a selector is different from interrupting a channel. The selector does not change any related channel. It only checks their status. When a thread sleep in the select () method is interrupted, the channel status is not ambiguous.
◇ Management selection key
The selection is cumulative. Once a selector adds a key to its selected key set, it will not remove the key. In addition, once a key is in the set of selected keys, the ready Set of this key will only be set and will not be cleared. At first glance, this may cause trouble, because the selection operation may not show the correct status of the registered channel. It provides great flexibility, but manages keys reasonably to ensure that the State information they represent is not handed over to programmers.
The secret to rational use of selectors is to understand the role played by the selection key set maintained by selectors. The most important part is what will happen when the key is no longer in the set of selected keys. When at least one operation of interest on the channel is ready, the ready Set of the key is cleared and the currently ready operation is added to the ready set. This key is then added to the set of selected keys.
To clear a SelectKey's ready Set, remove the key from the selected key set. The readiness status of the selection key is modified only when the selector object is selected. The processing idea is that only the keys in the selected key set are considered to contain valid ready information. This information will exist for a long time in the key until the key is removed from the selected key set to notify the selector that you have seen and processed it. If some operations of interest occur in the next channel, the key is reset to reflect the status of the current channel and added to the set of selected keys again.
This framework provides a lot of flexibility. The common practice is to call a select operation on the selector (this will update the set of selected keys) and traverse the set of keys returned by the selectKeys () method. In the process of checking each key in sequence, the related channels are also processed according to the ready set of keys. The key is then removed from the selected key set (by calling the remove () method on the Iterator object), and then check the next key. After that, repeat the loop by calling the select () method again.