The
Java nio:channels and buffers (channel and buffer)
Standard IO operates on byte streams and character streams. NIO is based on channels (Channel) and buffers (buffer), and the data is always read from the channel to the buffer or written to the channel from the buffer.
java nio:non-blocking io (non-blocking IO)
Java NIO allows you to use IO in a non-blocking way, for example: When a thread reads data from a channel to a buffer, Threads can still do other things. When data is written to the buffer, the thread can continue to process it. Writing a channel from a buffer is similar. The
java nio:selectors (selector)
Java NIO introduces the concept of selectors, which are used to monitor events for multiple channels (for example, the connection is open and the data arrives). As a result, a single thread can listen for multiple data channels.
Java NIO consists of the following core components:
channels
buffers
selectors
Although there are many other classes and components in Java NIO, it seems to me that Channel,buffer and Selector constitute the core API. Other components, such as pipe and filelock, are just tool classes that are used in conjunction with three core components. Therefore, I will focus on these three components in the overview. Other components are described in a separate section.
Channel and Buffer
Basically, all IO starts with a Channel in NiO. Channel a bit like a stream. Data can be read from channel to buffer or written to channel from buffer. Here's a diagram:
There are several types of channel and buffer. The following are the implementations of some of the major channel in Java NIO:
FileChannel
Datagramchannel
Socketchannel
Serversocketchannel
As you can see, these channels cover both UDP and TCP network IO, as well as file IO.
There are some interesting interfaces with these classes, but for the sake of simplicity, I try not to mention them in the overview. I will explain the other chapters of this tutorial that relate to them.
The following are key buffer implementations in Java NIO:
Bytebuffer
Charbuffer
DoubleBuffer
Floatbuffer
Intbuffer
Longbuffer
Shortbuffer
These buffer covers the basic data types you can send via IO: Byte, short, int, long, float, double, and Char.
Java NIO also has a mappedbytebuffer that represents the memory-mapped file, and I do not intend to explain it in the overview.
Selector
Selector allows multiple Channel to be processed in a single thread. If your application opens multiple connections (channels), but the traffic is low for each connection, it is convenient to use selector. For example, in a chat server.
This is a single thread that uses a selector to handle 3 channel diagrams:
To use selector, you have to register the channel with selector, and then call its select () method. This method blocks to a registered channel until the event is ready. Once this method returns, the thread can handle these events, with examples of events like new connections coming in, data receiving, and so on.
The channels of Java NiO are similar in flow, but somewhat different:
Both the data can be read from the channel and the data can be written to the channel. However, the flow of read and write is usually one-way.
Channels can read and write asynchronously.
The data in the channel is always read first to a buffer, or always to be written from a buffer.
As mentioned above, reads data from the channel to the buffer, writes data from the buffer to the channel. As shown in the following illustration:
The realization of Channel
These are the implementations of the most important channels in Java NIO:
FileChannel
Datagramchannel
Socketchannel
Serversocketchannel
FileChannel reads and writes data from the file.
Datagramchannel can read and write data from the network via UDP.
Socketchannel can read and write data from the network through TCP.
Serversocketchannel can listen for new incoming TCP connections, like a Web server. A socketchannel will be created for each new incoming connection.
Basic Channel Example
Below is an example of reading data to buffer using FileChannel:
Randomaccessfile afile = new Randomaccessfile ("Data/nio-data.txt", "RW");
FileChannel Inchannel = Afile.getchannel ();
Bytebuffer buf = Bytebuffer.allocate (a);
int bytesread = Inchannel.read (BUF);
while (Bytesread!=-1) {
System.out.println ("Read" + bytesread);
Buf.flip ();
while (buf.hasremaining ()) {
System.out.print ((char) buf.get ());
}
Buf.clear ();
Bytesread = Inchannel.read (BUF);
}
Afile.close ();
Note the call to Buf.flip (), first read the data to the buffer, then reverse the buffer, and then read the data from the buffer. The next section will delve into more details about buffer. The primary difference between
Java nio and io
When should I use IO and when to use NIO? In this article, I'll try to clearly parse the differences between Java NiO and Io, their usage scenarios, and how they affect your code design.
The main differences between
java nio and io
The following table summarizes the main differences between Java NiO and Io, and I describe the differences in each part of the table in more detail.
ion IO
oriented to stream buffer-oriented
blocking io non-blocking io
No selector
stream-oriented vs. buffer-oriented
The first major difference between Java NiO and Io is that IO is stream oriented and NIO is buffer-oriented. Java io-oriented streaming means that one or more bytes are read from the stream each time, until all bytes are read, and they are not slowed down anywhere. In addition, it cannot move data back and forth in the stream. If you need to move data that is read from the stream before and after, you need to first cache it to a buffer. Java NiO has a slightly different buffer-oriented approach. The data is read to a buffer it handles later, and can be moved back and forth in the buffer when needed. This increases the flexibility in the processing process. However, you also need to check whether the buffer contains all the data you need to work with. Also, make sure that when more data is read into the buffer, do not overwrite data that has not been processed in 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 fully written. The thread can no longer do anything in the meantime. Non-blocking mode of Java NiO, which allows a thread to send requests to read data from a channel, but it can only get the data that is currently available and will not get anything if no data is available at the moment. Instead of keeping the thread blocked, the thread can continue to do something else until the data becomes readable. The same is true for non-blocking writing. A thread requests to write some data to a channel, but does not need to wait for it to be fully written, and the thread can do something else at the same time. Threads typically use non-blocking IO idle time to perform IO operations on other channels, so a single thread can now manage multiple input and output channels (channel).
Selector (selectors)
The Java NiO selector allows a separate thread to monitor multiple input channels, you can register multiple channels using a selector, and then use a separate thread to "select" the channel: these channels already have the input that can be processed, or select the channel that is ready to be written. This selection mechanism makes it easy for a single thread to manage multiple channels.
How NiO and Io affect the design of an application
Whether you choose IO or the NIO toolbox, you may affect the following aspects of your application design:
API calls to NiO or IO classes.
Data processing.
The number of threads used to process the data.
API call
Of course, the API call using NIO may look different from using IO, but that's not surprising, since it's not just read from one inputstream to bytes, but the data must be read into the buffer before it is processed.
Data Processing
Data processing is also affected by the use of pure NIO design compared to IO design.
In IO design, we read the data byte by bit from InputStream or reader. Let's say you're working on a line based text stream, such as:
Name:anna
Age:25
Email:anna@mailserver.com
phone:1234567890
The flow of this line can be handled in this way:
BufferedReader reader = new BufferedReader (new InputStreamReader (input));
String nameline = Reader.readline ();
String ageline = Reader.readline ();
String emailline = Reader.readline ();
String phoneline = Reader.readline ();
Please note how long the processing status is determined by the program execution. In other words, once the Reader.readline () method returns, you will know that the line of text has been read, ReadLine () blocked until the whole line is read, that's why. You also know that this row contains a name; Similarly, the second readline () call returns when you know that the line contains age and so on. As you can see, the handler runs only when new data is read and knows what the data for each step is. Once a running thread has processed some of the data that is being read, the thread does not rollback the data (mostly). The following figure also illustrates this principle:
(Java IO: reading data from a blocked stream)The implementation of a NIO is different, and here's a simple example:
Bytebuffer buffer = bytebuffer.allocate (48);
int bytesread = inchannel.read (buffer);
Note that the second line reads bytes from the channel to Bytebuffer. When this method call returns, you don't know if all the data you need is in the buffer. What you know is that the buffer contains some bytes, which makes handling a bit difficult.
Assuming that after the first read (buffer) call, the data read into the buffer is only half a row, for example, "Name:an", can you handle the data? Obviously not, you need to wait until the entire row of data is read into the cache, before any processing of the data is meaningless.
So, how do you know if the buffer contains enough data to handle it? Well, you don't know. The discovered method can only view the data in the buffer. The result is that you have to check the buffer data several times before you know that all the data is in the buffer. This is not only inefficient, but also can make the program design scheme messy. For example:
Bytebuffer buffer = bytebuffer.allocate (48);
int bytesread = inchannel.read (buffer);
while (! bufferfull (bytesread)) {
Bytesread = inchannel.read (buffer);
}
The Bufferfull () method must track 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, it means that the buffer is full.
The Bufferfull () method scans the buffer, but must remain the same state before the Bufferfull () method is invoked. If not, the next data read into the buffer may not be able to read to the correct location. This is not possible, but it is another issue that needs to be noted.
If the buffer is full, it can be processed. If it is dissatisfied and meaningful in your actual case, you may be able to process some of the data. But in many cases this is not the case.