Java NIO

1. General

Java NiO (New IO) consists of the following three core components:

Generally, in NiO, IO starts with a channel. Data can be read from the channel to the buffer or written to the channel from the buffer. The selector allows a single thread to process multiple channels.

2. Channel

Channels is similar to streams, but there are some differences:

As described above, data is read from the channel to the buffer and written from the buffer to the channel, as shown in the following figure:

The four most important implementations of channel:

3. Buffer

In Java NiO, buffer is used to interact with the channel. Data is read from the channel to the buffer and written from the buffer to the channel.

Buffer is essentially a memory block in which data can be written and then read again later. The memory block is wrapped in a NiO buffer object, which provides a set of methods to use the memory block more easily.

Using buffer to read and write data is typically divided into four steps:

When you write data to a buffer, the buffer will track how much data you have written. Once you need to read data, you need to call the flip () method to switch the buffer from write mode to read mode. In read mode, buffer allows you to read all data written to the buffer.

Once you have read all the data, you need to clear the buffer so that it can be written to the data again. There are two ways to achieve this: clear () or compact (). The clear () method will clean up the entire buffer, and the compact () method will only clean up the data you have read. Any unread data will be moved to the beginning of the buffer, and the data written to the buffer in the future will be after the current unread data.

Buffer has three properties:

The meaning of position and limit depends on whether the buffer is in read mode or write mode. In either mode, the meaning of capacity is always the same.

Capacity

As a memory block, buffer has a fixed size, also known as "capacity". Once the buffer is full, it needs to be emptied (read data or clear data) before more data can be written to it.

Position

When you write data to the buffer, you need to write in a specific location. The initial position is 0. When data is written, position will move forward to point to the next writable position. The maximum value of position is capacity-1.

When you read data from the buffer, you also need to start reading from a given location. When you switch the buffer from write mode to read mode, position will be reset to 0.

Limit

In write mode, limit indicates how much data you can write to the buffer. In write mode, the value of limit is equal to capacity.

In read mode, limit indicates how much data you can read from the buffer. Therefore, when switching from write mode to read mode, limit is set to position in write mode. In other words, you can read as much as you write.

3.1. Allocate a buffer

In order to obtain a buffer, you must first allocate space to it. Each type of buffer has an allocate () method to do this.

3.2. Write data to buffer

There are two ways to write data to the buffer:

flip()

The flip () method switches the buffer from the write mode to the read mode. Calling flip () will set the position to 0, and the limit will remain the same as before.

3.3. Read data from buffer

There are two ways to read data from the buffer:

rewind()

Buffer. Rewind () sets position to 0 so that you can read all the data in the buffer from scratch.

clear()

The clear () method sets position to 0, and the limit is equal to capacity. In other words, the buffer is cleared. In fact, the data on the buffer is not really cleared. It just tells you where you can write the data.

compact()

The compact () method copies all unread data to the beginning of the buffer, then it sets position to the right of the last unread element, and the limit is still equal to capacity. Now, the buffer can be written, but you can't overwrite the unread data before.

Mark() and reset()

By calling buffer Mark() you can mark a given location. You can call Buffer. later. Reset () returns to the location just marked.

4. Selector

A selector is a component that checks one or more channel instances and determines which channels are ready to read or write. In this way, one thread can manage multiple channels to manage multiple network connections (PS: selelctor can determine which channels are readable or writable, so that only one thread can manage multiple network connections)

4.1. Why use selector

The advantage of using a single thread to process multiple channels is that fewer threads are required to process channels. In fact, you can use one thread to process all channels. For the operating system, switching between threads is very expensive, and each thread will occupy some resources (memory) in the operating system. Therefore, the fewer threads you use, the better. (PS: but remember, modern operating systems and CPUs are getting better and better in multitasking, so the overhead of multithreading will become smaller and smaller over time.)

4.2. Create selector

Channel must be in non blocking mode to be used with selector. This means that filechannel cannot be used with selector because filechannel cannot be switched to non blocking mode.

The second parameter of the register () method indicates the events you want to listen to in the channel through the selector. There are four different events that can be monitored:

These four events are represented by four constants of selectionkey:

If you are interested in multiple events, you can write as follows:

4.3. Select the channel through the selector

After calling any select () method, the channel that you are interested in and the corresponding event is ready will be returned to you. In short, if you are interested in a channel ready for reading, you will receive such a channel from the select () method.

The return value of the select () method is an int value indicating how many channels are ready. That is, how many channels have become ready since the last call to select ().

Complete example:

5. SocketChannel

Java NiO socketchannel is a channel connected to the TCP network socket.

There are two ways to create socketchannel:

5.1. Read from socketchannel

SocketChannel. The read () method reads the data from the socketchannel to the buffer, and its return value indicates how many bytes are written to the buffer. If - 1 is returned, the end of the stream is reached.

5.2. Write data to socketchannel

Note that socketchannel Write () is placed in the body of the while loop. Since there is no guarantee how many bytes the write () method will write to the socketchannel, call the write () method repeatedly until there are no bytes to write to the buffer.

5.3. Non blocking mode

When a socketchannel is set to non blocking mode, you can call the connect (), read (), write () method asynchronously.

connect()

If socketchannel is in non blocking mode, when you call the connect () method, the method may return before establishing the connection. To determine whether the connection has been successfully established, you can call the finishconnect () method.

Write() and read()

In non blocking mode, the write () method may return without writing any content, so write () is required to be invoked in the loop. Similarly, in non blocking mode, the read () method may return without reading any data. Therefore, pay attention to the int returned, which tells us how many bytes have been read.

6. ServerSocketChannel

Java NiO serversocketchannel is a channel that can listen for incoming TCP connections, just like ServerSocket in standard Java network.

6.1. Listening for Incoming Connections

By calling serversocketchannel The accept () method listens for incoming connections. When the accept () method returns, it returns a socketchannel with an input connection. Therefore, accept () blocks until an input connection arrives. The usual practice is as follows:

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>