NIO knowledge excerpt

In JDK 1.4, the NIO (New Input/ Output) class was newly added, which introduced an I/O method based on channels and buffers. It can use the Native function library to directly allocate off-heap memory, and then store it in a The DirectByteBuffer object of the Java heap operates as a reference to this memory, avoiding copying data back and forth between the Java heap and the Native heap.

 

    NIO is a synchronous non-blocking IO model. Synchronization means that the thread continuously polls whether the IO event is ready, and non-blocking means that the thread can do other tasks at the same time while waiting for IO. The core of synchronization is Selector. Selector replaces the thread itself to poll IO events, avoiding blocking and reducing unnecessary thread consumption; the core of non-blocking is the channel and buffer. When the IO event is ready, you can write the buffer by writing , to ensure the success of IO without thread blocking waiting.

 

Buffer:

 

    Why is NIO a buffer-based IO method? Because, when a link is established, the IO data may not arrive immediately. In order to correctly complete the IO operation when the data arrives, in BIO (blocking IO), the thread waiting for IO must be blocked to perform IO operations around the clock. . In order to solve the problem of inefficient IO mode, the concept of buffer is introduced. When data arrives, it can be written into the buffer in advance, and then handed over to the thread by the buffer, so the thread does not need to block waiting for IO.

 

aisle:

 

    When executing: SocketChannel.write(Buffer), a buffer is written to a channel. If the buffer is easy to understand, the channel is relatively more abstract. It is inevitable that online blogs are not rigorously written, which can easily make it difficult for beginners to understand.

 

    To quote the authoritative statement in Java NIO: A channel is the entry through which I/O transfers take place, and a buffer is the source or destination of those data transfers. For transfers out of the buffer, the data you want to pass out is placed in a buffer and sent to the channel. For transfers back into the buffer, a channel places the data in the buffer you provide.

 

    For example, there is a server channel ServerSocketChannel serverChannel, a client channel SocketChannel clientChannel; server buffer: serverBuffer, client buffer: clientBuffer.

 

    When the server wants to send data to the client, it needs to call: clientChannel.write(serverBuffer). When the client wants to read, call clientChannel.read(clientBuffer)

 

    When the client wants to send data to the server, it needs to call: serverChannel.write(clientBuffer). When the server wants to read, call serverChannel.read(serverBuffer)

 

    In this way, the relationship between channels and buffers seems to be better understood. In practice, there may not be such a stupid thing about bidirectional connection (however, it does exist, and the content will be covered later), but it can be understood as in NIO: if you want to send Data to the destination, you need to store the data The Buffer of Data is written to the Channel of the target end, and then the data is read from the Channel to the Buffer of the target end.

 

Selector:

 

    The mechanism of channels and buffers allows threads to wait for IO events to be ready without blocking, but there is always someone to supervise these IO events. This work is handed over to the selector to complete, which is called synchronization.

 

    Selector allows a single thread to process multiple Channels. Using Selector is handy if your app has multiple connections (channels) open, but each connection has low traffic.

 

    To use the Selector, register the Channel with the Selector and then call its select() method. This method will block until a registered channel has an event ready, which is called polling. Once this method returns, the thread can handle these events.

 

    The events of interest registered in the Selector are:

 

ON_ACCEPT

 

ON_CONNECT 

 

ON_READ 

 

OP_WRITE

 

optimization:

 

    An optimization method is to further decompose the Selector into Reactors, and separate different events of interest. Each Reactor is only responsible for one event of interest. The advantages of this are: 1. Separating the blocking level and reducing the polling time; 2. The thread does not need to traverse the set to find the events of its own interest, because the obtained set only contains the events of its own interest.

 

 

 

NIO and epoll:

 

    epoll is the IO model of the Linux kernel. I think someone must want to ask, AIO sounds more advanced than NIO, why not use AIO? AIO actually has applications, but there is a problem that Linux does not support AIO, so the efficiency of AIO-based programs running on Linux is lower than that of NIO. Linux is the most important server OS, so NIO is currently more widely used than AIO.

 

    Having said that, you may have understood that epoll must have a deep relationship with NIO. Yes, if you carefully study the technical insider of epoll, you will find that it is indeed very similar to NIO, based on "channels" and buffers, as well as selectors, but in epoll, channels are actually "pipes" of the operating system . Unlike NIO, in NIO, threads are liberated, but the selector needs to poll for the readiness of IO events in a blocking manner; while in epoll, after the IO events are ready, a message is automatically sent to notify the selector: "I'm ready. "It can be considered that Linux's epoll is a more efficient NIO.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326093605&siteId=291194637