Study notes of JAVA NIO - architecture briefings
Created#More Posted time:Dec 21, 2016 17:39 PM
Recently I have encountered a lot of NIO-related content. It can be said that NIO is a blind zone of mine, as I was less exposed to this field before. So I decided to spend some time learning it. Here are my personal study notes.
NIO (New IO) is a brand new IO API launched after JDK1.4. It adopts new underlying I/O models compared with the traditional IO mode. Traditional IO is designed to be stream-oriented, while NIO is block-oriented. In simple words, traditional I/O is based on bytes, and all I/O requests are regarded as moving single bytes. The object should be converted to bytecode first for usage. While the NIO targets blocks, and processes data in blocks. Every operation will generate or consume a data block. From the designing philosophy, NIO has a much larger operation granularity than traditional IO does. NIO can greatly improve the performance, but also sacrifices the elegance and conciseness of Java operations.
Java NIO is composed of the three core modules as follows:
The thread overhead is large. Selector can help developers to break through the IO bottleneck and provides a mechanism for processing multiple channels in a single thread. If your application opens multiple connections (channels), but every one of them has a low traffic flow, it would be very convenient to use Selector.
This is a diagram showing using a selector to process three channels in a single thread:
To use Selector, you should first register the channel to the selector, and then call its select() method. This method will be congested until a registered channel has an event ready. Once this method is returned, the thread can process these events. Examples of the events include creating a new connection, or receiving data.
To use Selector, you should first register the channel to the selector, and then call its select() method. This method will be congested until a registered channel has an event ready. Once this method is returned, the thread can process these events.
Channel is an object for reading and writing data. All IO requests start from a channel in NIO. Data in a channel is either written to the buffer or read from the buffer. If we compare NIO with the original I/O, the channel is like the stream. All data is processed by buffer objects. NIO won't write bytes into a channel directly. On the contrary, it writes data into the buffer zone with one or more bytes. Similarly, you won't read bytes from the channel directly. Instead, the data is read into the buffer zone from the channel, and you read the byte from the buffer zone.
Channel and stream are different in that the channel is bidirectional, while the stream only flows in one direction (a stream must be the sub class of either an InputStream or an OutputStream). The channel can be used for reading or writing, or reading and writing at the same time. Therefore, the channel can better reflect the real conditions of the underlying operating system than the stream does. This is especially the case in the UNIX model where channels of the underlying operating system are bidirectional.
• ServerSocketChannel: monitors TCP connections
The relationship between the buffer and channel is shown in the figure below:
Buffer is a highlight of NIO. The data-stream-oriented traditional IO operations mean the data is not cached during the whole processing, from the time one or more bytes are read from the stream to the operation’s completion. NIO operations target the buffer zone. Data is read from the channel into the buffer zone, and then processed in the buffer zone.
Buffer is in fact a buffer zone. It stores data using byte arrays internally and maintains several special variables to achieve repeated use of data.
Currently, Buffer has the following implementation classes:
These are all abstract classes. There are many powerful sub-classes targeting their respective application scenarios.
The NIO-related scenarios we encounter in work usually have encapsulated third-party libraries ready. But this doesn't render learning its implementation principles unnecessary. Knowing how without knowing why makes us feel insecure after all.
What’s more, learning about outstanding open-source projects is an effective way for personal ability building. To study the source code, we can start from the following aspects (my personal opinions):
• Architecture documents (establish a framework graph in mind)
• Example code and unit tests (understand the details for using specific interfaces)
• Implementation code of specific modules (data structure - public method - private method)