Selectable Channels
By default, channels (and streams) block. That is, when you write to or read from the channel, the current thread stops until the writing or reading is complete. (The same is true for connecting and accepting connections, but I'm just going to say writing and reading from here on and let the connecting and accepting be understood.) In nonblocking mode, by contrast, the read or write happens as fast as the hardware allows. It doesn't take absolutely zero time, but it goes as quickly as it possibly can. If the thread has to wait for more data to arrive from the network, for an Ethernet card buffer to be released by some other process, or for some other relatively long-lasting operation, it instead returns having read or written only some or even none of the bytes it was asked to read or write. Your program needs to keep track of how many bytes have been read or written and try again with the bytes that haven't been read or written. If the program doesn't have anything else to do, it might as well operate in blocking mode. However, if the program does have something else it can do in the meantimefor instance, a network server might process a different connectionthen this is worthwhile.
Not all channels support nonblocking I/O. Network channels do, but file channels don't. Those channels that do support nonblocking I/O are all subclasses of the java.nio.channels.SelectableChannel class.
All channels are created in blocking mode. To switch a channel to nonblocking mode, you pass false to the configureBlocking( ) method:
public abstract SelectableChannel configureBlocking(boolean block) throws IOException
This is normally the first thing you do after opening the channel. You can actually change a channel from blocking to nonblocking or vice versa later in its life if it's not currently associated with any Selectors, but this is unusual.
Once you've put a channel in nonblocking mode, you don't read or write it immediately. Instead, you register a Selector with the channel. You then ask the Selector if the channel is ready for reading or writing. If the Selector says, "Yes, the channel is ready," you go ahead and read it or write it. Otherwise, you do something else. (A little more accurately, you ask the Selector which of its channels are ready for some operation and you operate on the channels the Selector says are ready. You don't ask it about individual channels.)
The register( ) method registers a Selector with the channel:
public abstract SelectionKey register(Selector selector, int operations) throws ClosedChannelException
Each channel can be registered only once with any given Selector object. Otherwise, however, there is a many-to-many relationship between Selectors and channels. Each Selector normally monitors many different channels, and each channel may be registered with several different Selectors. The most common usage pattern, though, is that each channel registers only a single Selector.
The second argument specifies the operations for which the Selector should select. There are four: reading, writing, accepting, and connecting. Server socket channels normally register only for accepting. Socket channels register for any or all of the other three. These operations are represented as named constants in the SelectionKey class. These constants follow the usual powers of two pattern, so you can register for more than one operation using the bitwise or operator. For example, this statement registers a channel for both reading and writing:
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
A second overloaded version of the register( ) method adds a third argument for an attachment:
public abstract SelectionKey register( Selector selector, int operations, Object attachment) throws ClosedChannelException
Neither the channel nor the Selector uses the attachment object, and it can be null. It's there for your own use. Most programs use it to store a ByteBuffer or other object that tracks the data being written to or read from the channel.
Both register( ) methods return a SelectionKey object that represents the unique connection between this channel and this Selector. More often than not, however, the return value is ignored. The Selector gives you back the key when you need it.
If you need to change the operations of interest (e.g., changing reading to writing, or read-only to read/write), you can call the register( ) method a second time. This also changes the attachment, but it does not change the key. The same key is always returned for a particular Selector/SelectableChannel pair. If the key has already been cancelled, reregistering throws a CancelledKeyException.
The register( ) and configureBlocking( ) methods are threadsafe, but they may block for a short period of time when used concurrently. You can also synchronize your own code on the same lock object, which is returned by the blockingLock( ) method:
public abstract Object blockingLock( )
Several getter methods correspond to the setter methods. The isBlocking( ) method returns true if a channel is in blocking mode and false otherwise:
public abstract boolean isBlocking( )
The isRegistered( ) method returns true if one or more Selectors are registered with this channel and false otherwise:
public abstract boolean isRegistered( )
The keyFor( ) method returns the SelectionKey corresponding to a particular Selector:
public abstract SelectionKey keyFor(Selector sel)
The validOps( ) method returns a combination of bit flags specifying which operations are and are not available to this channel:
public abstract int validOps( )
It's rare to need any of these. Most of the time your own code creates the SelectableChannel objects you use, and you know exactly what state they're in.