Main Differences Betwen Java NIO and IO:
The table below summarizes the main differences between Java NIO and IO. I will
get into more detail about each difference in the sections following the table.
IO | NIO |
Stream oriented | Buffer oriented |
Blocking IO | Non blocking IO Selectors |
Stream Oriented vs. Buffer Oriented:
The first big difference between Java NIO and IO is that IO is stream
oriented, where NIO is buffer oriented. So, what does that mean?
Java IO being stream oriented means that you read one or more bytes at a time,
from a stream. What you do with the read bytes is up to you. They are not cached
anywhere. Furthermore, you cannot move forth and back in the data in a stream.
If you need to move forth and back in the data read from a stream, you will need
to cache it in a buffer first.
Java NIO's buffer oriented approach is slightly different. Data is read into a
buffer from which it is later processed. You can move forth and back in the
buffer as you need to. This gives you a bit more flexibility during processing.
However, you also need to check if the buffer contains all the data you need in
order to fully process it. And, you need to make sure that when reading more
data into the buffer, you do not overwrite data in the buffer you have not yet
processed.
Blocking vs. Non-blocking IO:
Java IO's various streams are blocking. That means, that when a thread invokes a
read() or write(), that thread is blocked until there is some data to read, or
the data is fully written. The thread can do nothing else in the meantime.
Java NIO's non-blocking mode enables a thread to request reading data from a
channel, and only get what is currently available, or nothing at all, if no data
is currently available. Rather than remain blocked until data becomes available
for reading, the thread can go on with something else.
The same is true for non-blocking writing. A thread can request that some data
be written to a channel, but not wait for it to be fully written. The thread can
then go on and do something else in the mean time.
What threads spend their idle time on when not blocked in IO calls, is usually
performing IO on other channels in the meantime. That is, a single thread can
now manage multiple channels of input and output.
Selectors:
Java NIO's selectors allow a single thread to monitor multiple channels of
input. You can register multiple channels with a selector, then use a single
thread to "select" the channels that have input available for processing, or
select the channels that are ready for writing. This selector mechanism makes it
easy for a single thread to manage multiple channels.
How NIO and IO Influences Application Design:
Whether you choose NIO or IO as your IO toolkit may impact the following aspects
of your application design:
The API calls to the NIO or IO classes.
The processing of data.
The number of thread used to process the data.
The API Calls
Of course the API calls when using NIO look different than when using IO. This
is no surprise. Rather than just read the data byte for byte from e.g. an
InputStream, the data must first be read into a buffer, and then be processed
from there.
The Processing of Data
The processing of the data is also affected when using a pure NIO design, vs. an
IO design.
In an IO design you read the data byte for byte from an InputStream or a Reader.
Imagine you were processing a stream of line based textual data. For instance:
InputStream input = ... ; // get the InputStream from the client socket
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
String nameLine = reader.readLine();
String ageLine = reader.readLine();
String emailLine = reader.readLine();
String phoneLine = reader.readLine();
Notice how the processing state is determined by how far the program has
executed. In other words, once the first reader.readLine() method returns, you
know for sure that a full line of text has been read. The readLine() blocks
until a full line is read, that's why. You also know that this line contains the
name. Similarly, when the second readLine() call returns, you know that this
line contains the age etc.
The number of thread used to process the data
As you can see, the program progresses only when there is new data to read, and for each step you know what that data is. Once the executing thread have progressed past reading a certain piece of data in the code, the thread is not going backwards in the data (mostly not).
Read More →