Before J2SE 1.4, we could simulate non-blocking I/O by using method available of
class InputStream . The signature for this method is as follows:
int available() throws IOException
For an InputStream object attached to a network connection, this method returns
the number of bytes received via that connection (and now in memory), but not yet
read. In order to simulate non-blocking I/O, we could create a separate connection
(on the same port) for each incoming client and repeatedly 'poll' clients in turn,
using method available to check for data on each connection. However, this is a
poor substitute for true non-blocking I/O and has never been used much.
J2SE 1.4 introduced the New Input/Output API, often abbreviated to NIO. This
API is implemented by package java.nio and a handful of sub-packages, the most
notable of which is java.nio.channels . Instead of employing Java's traditional stream
mechanism for I/O, NIO makes use of the channel concept. Essentially, rather than
being byte-orientated, as Java streams are, channels are block- orientated
means that data can be transferred in large blocks, rather than as individual bytes,
leading to signifi cant speed gains. As will be seen shortly, each channel is associated
with a buffer , which provides the storage area for data that is written to or read from
a particular channel. It is even possible to make use of what are called direct buffers ,
which avoid the use of intermediate Java buffers wherever possible, allowing system
level operations to be performed directly, leading to even greater speed gains.
Of greater relevance to the title of this section, though, is the mechanism for
handling multiple clients. Instead of allocating an individual thread to each client,
NIO uses multiplexing (the handling of multiple connections simultaneously by a
single entity). This is based on the use of a selector (the single entity) to monitor
both new connections and data transmissions from existing connections. Each of
our channels simply registers with the selector the type(s) of event in which it is
interested. It is possible to use channels in either blocking or non-blocking mode,
but we shall be using them in non-blocking mode. The use of a selector to monitor
events means that, instead of having a separate thread allocated to each connection,
we can have one thread (or more, if we wish) monitoring several channels at once.
This avoids problems such as operating system limits, deadlocks and thread safety
violations that may occur with the one thread per connection approach.
Though the multiplexing approach offers signifi cant advantages over the multi-
threaded one, its implementation is notably more complex. However, most of the
original I/O classes have, in fact, been redesigned to use channels as their underly-
ing mechanism, which means that developers may reap some of the benefi ts of NIO
without changing their programming. If greater speed is required, though, it will be
necessary to employ NIO directly. The next sub-section provides the necessary
detail to allow you to do this.