Java core technology reading notes 11-3 introduction to Java NiO and overview of core functions

1. What is NIO?

Java NIO(New IO) was introduced in Java 1.4. This API can implement a completely different mechanism from traditional io. For NIO, it mainly provides a new multiplexing IO technology for the network. In network oriented data transmission, if there are many connections and the amount of data transmitted each time is not very large, then this scenario will be more suitable for using NIO technology.

Note that NIO of Java NIO is not an abbreviation for non blocking I/O. For one thing, not all its read-write threads are non blocking. Second, non blocking IO needs to continuously occupy the processor for polling, which is not suitable for concurrency. Java NIO uses multiplexing technology.

2. Difference between traditional IO and NIO

First, at the bottom, traditional I/O is stream oriented, reading and writing one or more bytes at a time. In a stream, random access to data is not allowed.
For NIO, the buffer is transmitted in blocks. After using the buffer, the read-write position can be moved. The transmission tool uses channels. Compared with unidirectional streams, channels can read and write in both directions.
Secondly, in the IO model, the traditional I/O is the synchronous blocking IO model, while NIO uses the multiplexing technology. You can see the IO underlying principle and IO model: Preliminary knowledge of Java NIO: I/O underlying principle and network I/O model.
In addition, NIO can use only one thread to manage multiple channels for reading and writing data. The biggest advantage of IO multiplexing is low system overhead.
Finally, using traditional IO and combining streams can make it easier to deal with complex file contents, but some API s included in NIO also support more convenient processing of the whole file.

3. Overview of NiO core components

NIO has three core components: Selector, Channel and Buffer.
3.1 Selector
After understanding what multiplexing is, we know that to implement this mechanism, there must be a component that can track all Socket states. In NIO, this component is the Selector.

3.2 Channel
In addition, in NIO, reading and writing data must go through a Channel, that is, Channel. Data is transmitted through the Channel, which is somewhat like a stream, but the Channel is bidirectional and supports non blocking read and write of NIO.

Some implementation classes of channel:
·FileChannel reads and writes data from a file
·Datagram channel reads and writes data through UDP
·SocketChannel reads and writes data through TCP
·ServerSocketChannel listens to TCP connections and provides a SocketChannel for each TCP connection
Establish different channels to connect the channels to hard disk files or networks.

3.3 Buffer
Buffer is an area in memory used as a buffer for user processes to read and write. When using Channel, data is either read into the buffer (write operation) or written out from the buffer (read operation).
Therefore, for the read operation of the buffer, you can call the read method of the Channel object to read data from the Channel, or you can call the put method of the buffer to directly put data into the program.
For the write operation of the buffer, you can call the write method of the Channel object to write the buffer data to the Channel, or you can call the get method of the buffer to directly return the buffer data.

Some implementation classes of Buffer:
· ByteBuffer
· CharBuffer
· DoubleBuffer
· FloatBuffer
· IntBuffer
· LongBuffer
· ShortBuffer
It covers the basic data types except boolean type. In addition, there is a MappedByteBuffer to represent the memory sample file.

capacity, position and limit of buffer
Capacity: its values represent the allocated capacity when constructing the buffer. According to different buffers, they represent the number of bytes, chars, etc. For example, LongBuffer.allocate(8) indicates that 8 Long sizes are allocated.

Position: its value indicates the current operation position of the buffer. Position is equivalent to the following index, ranging from 0 to capacity-1. Initialization, calling flip method and clear method will set it to 0. Calling the compact method can empty the read data, then move all unread data forward to fill the gap, and position is the subscript + 1 of the last element. Every time you use the read or put method to put an element into the buffer, you start with the subscript indicated by position. After the element is placed, position+1. Each time you use the get method, you will return the element of the current position and make position+1.
In addition, the mark/reset method can also be used to mark the position and set the position as a mark.

Note that each element cell is a buffer type, which is similar to an array. For example, each element cell of charBuffer is a char, and position backward represents moving one cell backward.

Limit: its value indicates how many elements are readable in the current buffer. After initializing and calling the clear, compact and rewind methods, the limit will be set to capacity. Only by calling the flip method will the limit be set to the position before the call. Therefore, when the buffer reads data and wants to write data from the buffer (whether using get or write), it is best to use the flip method to make the buffer ready for writing.

A basic code example including Channel and Buffer:

    public static void main(String[] args) throws IOException {
        //The contents of the file are: aaaaaa \ nbbbb the file has a total of eight bytes of a plus a byte newline character \ NAND four bytes of b, a total of thirteen bytes
        RandomAccessFile raf = new RandomAccessFile(new File("file/Test.txt"), "rw"); //Create a random access file
        FileChannel inChannel = raf.getChannel(); //Build a Channel connecting files
        ByteBuffer buf = ByteBuffer.allocate(8); //Build a byte buffer with a buffer size of 8 bytes
        int read = inChannel.read(buf); //The buffer reads data from the channel. The maximum number of bytes read is the size of the buffer. If it is insufficient, it is the actual number of bytes. This method returns the number of bytes read in
        while (read != -1){ //When there is read data in the buffer
            System.out.println("Bytes read:" + read); //The buffer size is 8 and will be read twice. The first time is output 8 and the second time is output 5
            buf.flip(); //Make the buffer ready to write out data, set limit = position, position=0
            //The value returned by this method is position < limit. Obviously, returning true means that there are still elements readable in the current position
            //The number of remaining elements is limit - position
            while (buf.hasRemaining()){
                System.out.print((char)buf.get()); //Output the byte pointed to by position
            }
            System.out.println();
            buf.clear(); //Empty buffer, position = 0, limit = capacity
            read = inChannel.read(buf); //Try reading again
        }
        raf.close();
    }

equals and compareTo methods of butters
The equals method compares the elements in the range of [position,limit) in two buffers. The number, type and value must be the same before returning true. This method is overridden by the Object method
The compareTo method also compares the elements in the [position,limit) range of the two buffers. This method traverses the elements in the range of the two buffers in turn and calls Byte.compare (byte a, byte b) Method for comparison. Where a is the element currently traversed by the caller, and B is the element currently traversed by the parameter buffer. If the two elements are the same, compare the next one, otherwise directly return a - b. if the length of the two buffers does not match, return the difference between the lengths of the two buffer ranges. If the size and elements of the two buffers are exactly the same, return 0 . this method is implemented by the Comparable interface.

Channel reading and writing to multiple butters
Scattering Reads
Using the read method of the Channel object, you can take the butterfly array as a parameter, and then read the data in the Channel into these buffers. When the first buffer in the array is full, you can read the second buffer, and so on. This method is not suitable for dynamic messages, but suitable for fixed length data. For example, fixed length message headers and message bodies can use the corresponding length respectively Degree buffer to receive.

Gathering Writes
Using the write method of the Channel object, you can also take the Buffer array as a parameter. On the contrary, this method can write out the data in the [position,limit) range of the Buffer in the array to the Channel in order.
The example code is as follows:

    public static void main(String[] args) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(new File("file/Test.txt"), "rw"); //Create a random access file
        FileChannel inChannel = raf.getChannel(); //Build a Channel connecting files
        ByteBuffer b1 = ByteBuffer.allocate(8);
        ByteBuffer b2 = ByteBuffer.allocate(5);
        ByteBuffer[] byteBuffers = {b1, b2};
        //Channel data is read into two buffers
        inChannel.read(byteBuffers);
        printBufByte(b1, "b1");
        printBufByte(b2, "b2");

        //Write data from the two buffers to the Channel
        b1.put((byte)'b');
        b1.put((byte)'1');
        b2.put((byte)'b');
        b2.put((byte)'2');
        b1.flip(); //Update pointer
        b2.flip(); 
        inChannel.write(byteBuffers);
        inChannel.write(b1);
    }

Data transmission between channels
If there is a FileChannel, you can use this Channel to read and write data directly with other channels, that is, you can write the file content connected to this FileChannel directly to the source connected to other channels, or vice versa. The methods used are transerFrom and transerTo.

    public static void main(String[] args) throws IOException {
        RandomAccessFile raf1 = new RandomAccessFile(new File("file/Test.txt"), "rw"); //Create a random access file
        RandomAccessFile raf2 = new RandomAccessFile(new File("file/Test2.txt"), "rw"); //Create a random access file
        FileChannel inChannel1 = raf1.getChannel(); //Build a Channel connecting files
        FileChannel inChannel2 = raf2.getChannel(); //Represents another Channel. In fact, this Channel can be any other Channel
        //inChannel1 transmits data to inChannel2
        inChannel1.transferTo(1, inChannel1.size(), inChannel2); //The first parameter represents where the transmission starts from inChannel1. Here is the first byte, and the second parameter is the maximum number of bytes transmitted
        //inChannel1 receives the transmitted data from inChannel2
        inChannel1.transferFrom(inChannel2, 0, inChannel2.size()); //The meaning of the parameter is the same as that of transerTo
    }


Added by shortcircuit on Mon, 29 Nov 2021 00:46:45 +0200