The difference between Java NIO and IO

  • 2020-04-01 03:22:32
  • OfStack

The following table summarizes the major differences between Java NIO and IO, and I'll describe the differences in each section of the table in more detail.


IO                NIO
 Facing the flow              Facing the buffer 
 blocking IO             non-blocking IO
 There is no                  The selector 

Flow oriented versus buffering oriented

The first big difference between Java NIO and IO is that IO is stream-oriented and NIO is buffer-oriented. Java IO is stream-oriented meaning that one or more bytes are read from the stream at a time until all bytes are read, and they are not cached anywhere. Furthermore, it cannot move the data in the stream back and forth. If you need to move the data read from the stream back and forth, you need to cache it into a buffer first. Java NIO's buffer-guided approach is slightly different. The data is read into a buffer that it processes later and can be moved back and forth in the buffer as needed. This increases the flexibility of the process. However, you also need to check that this buffer contains all the data you need to process. Also, make sure that you don't overwrite the unprocessed data in the buffer as more data is read into the buffer.

Blocking and non-blocking IO

The various streams of Java IO are blocked. This means that when a thread calls read() or write(), the thread is blocked until some data is read, or the data is completely written. The thread cannot do anything else during this time. Java NIO's non-blocking mode enables a thread to send a request to read data from a channel, but it only gets the data that is currently available, and if no data is currently available, it gets nothing. Instead of keeping the thread blocked, the thread can continue to do other things until the data becomes readable. The same is true for non-blocking writes. A thread that requests to write some data to a channel, but does not have to wait for it to be fully written, can do something else at the same time. Threads typically spend their non-blocking IO free time performing IO operations on other channels, so a single thread can now manage multiple input and output channels.

Selectors

Java NIO's selectors allow a single thread to monitor multiple input channels. You can register multiple channels to use a single selector, and then use a single thread to "select" channels: those channels that already have input that can be processed, or those that are ready to be written. This selection mechanism makes it easy for a single thread to manage multiple channels.

How do NIO and IO affect application design

Whether you choose the IO or NIO toolkit, it may affect the following aspects of your application design:

1. API calls to NIO or IO classes.
2. Data processing.
3. Number of threads used to process data.

API call

Of course, the API calls with NIO look different from the ones with IO, but that's not surprising, because instead of just reading from an InputStream verbatim segment, the data must be read into the buffer before being processed.

The data processing

Data processing is also affected by using pure NIO design compared to IO design.

In the IO design, we read data from an InputStream or Reader verbatim section. Suppose you are working on a line-based text data stream, for example:


Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890

The stream of this line can be treated as follows:


BufferedReader reader = new BufferedReader(new InputStreamReader(input));

String nameLine   = reader.readLine();
String ageLine    = reader.readLine();
String emailLine  = reader.readLine();
String phoneLine  = reader.readLine();

Note that the processing state is determined by how long the program executes. In other words, once the reader.readline () method returns, you know for sure that the text line has been read, and readLine() blocks until the entire line has been read, which is why. You also know that this row contains the name; Again, when the second readline() call returns, you know that line contains ages and so on. As you can see, the handler runs only when new data is read in and knows what the data is for each step. Once a running thread has processed some of the data it reads in, the thread does not rewind the data (most of the time). The following diagram also illustrates this principle:

< img SRC = "border = 0 / / files.jb51.net/file_images/article/201406/20146485257088.png? 20145485326 ">
(Java IO: read data from a blocked stream) whereas an implementation of NIO would be different, here's a simple example:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

Notice the second line, reading the bytes from the channel to the ByteBuffer. When the method call returns, you don't know if all the data you need is in the buffer. All you know is that the buffer contains some bytes, which makes processing a bit difficult.
Suppose that after the first read(buffer) call, only half a line of data is read into the buffer, for example, "Name:An", can you handle the data? Obviously not, you need to wait until the entire row is read into the cache, before which any processing of the data is meaningless.

So, how do you know if the buffer contains enough data to handle? Well, you don't know. The discovered method can only view the data in the buffer. As a result, you must check the buffer a few times before you know that all the data is in the buffer. This is not only inefficient, but can clutter up your programming plans. Such as:


ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

while(! bufferFull(bytesRead) ) {

bytesRead = inChannel.read(buffer);

}

The bufferFull() method must keep track of how much data is read into the buffer and return true or false, depending on whether the buffer is full. In other words, if the buffer is ready to be processed, the buffer is full.

The bufferFull() method scans the buffer, but must remain in the same state until the bufferFull() method is called. If not, the next data read into the buffer may not be read to the correct location. This is impossible, but it is another problem to be aware of.

If the buffer is full, it can be processed. If it's not satisfactory, and makes sense in your actual case, you might be able to process some of that data. But in many cases this is not the case. The following figure shows "buffer data loop ready" :
< img SRC = "border = 0 / / files.jb51.net/file_images/article/201406/20146485350990.png? 2014548541 ">

3) number of threads used to process data

NIO lets you manage multiple channels (network connections or files) with just one (or several) single thread, but at the cost of parsing the data can be more complex than reading from a blocked stream.

If you need to manage thousands of connections that open at the same time, sending only a small amount of data at a time, such as a chat server, a server that implements NIO can be an advantage. Also, if you need to maintain many open connections to other computers, such as a P2P network, using a single thread to manage all your outbound connections can be an advantage. One thread multiple connection design such as

< img SRC = "border = 0 / / files.jb51.net/file_images/article/201406/20146485416362.png? 20145485426 ">

Java NIO: single thread manages multiple connections


If you have a small number of connections using very high bandwidth and sending large amounts of data at once, perhaps a typical IO server implementation might fit well. The following figure illustrates a typical IO server design:

< img SRC = "border = 0 / / files.jb51.net/file_images/article/201406/20146485444521.png? 20145485453 ">
Java IO: a typical IO server design - a connection is handled through a thread


 


Related articles: