Java IO stream (2) IO model (BIO|NIO|AIO)

Overview

The Java IO model includes synchronous blocking IO (BIO), synchronous non-blocking IO (NIO), and asynchronous non-blocking IO (AIO/NIO2). BIO, NIO and AIO in Java are understood to be the various IO models of the operating system implemented by the Java language. encapsulation

IO model

BIO(Blocking I/O)

Overview

BIO is a synchronization and blocking mode. The server implementation mode is one connection and one thread. That is, when the client has a connection request, the server needs to start a thread for processing. If the connection does not do anything, unnecessary thread overhead will be caused. Of course it can be improved through the thread pool mechanism, but the essential shortcomings have not been improved ( Java IO flow (1) IO basic practice codes are all based on BIO )

Applicable scene

It is suitable for fixed architectures with a relatively small number of connections ( less than 1000 per machine ). This method has relatively high requirements for server resources, and concurrency is limited to applications. It was the only choice before JDK1.4, but the program is intuitive, simple and easy to understand.

NIO(New I/O)

Overview

NIO is a synchronous non-blocking mode. It is a new IO API introduced from JDK1.4. It can replace the standard BIO. NIO supports buffer-oriented, Channel-based IO operations (Channel is responsible for transmission and Buffer is responsible for storage ) . Read and write files in a more efficient way

Implementation process

# selector+selectionKey(SocketChannel+selector)可监听连接、读、写
1.服务端:启动,新建ServerSocketChannel,注册到selector,生成selectionKey(ServerSocketChannel+selector),负责监听连接事件。
2.客户端:启动,新建SocketChannel和selector,然后与服务端端口建立连接。
3.服务端:selector监听到连接,取出第1步的selectionKey,取到ServerSocketChannel,用ServerSocketChannel新建一个SocketChannel,注册到selector负责监听读操作。
4.客户端:建立连接成功后,把SocketChannel注册到客户端的selector,生成selectionKey,负责监听连接事件。
5.客户端:监听到第4步连接成功。取出第4步新建的selectionKey,取出SocketChannel,向该Channel写入"HelloServer",并把该Channel注册到selector,负责监听读事件。
6.服务端:第3步中监听读事件的selector,监听到第5步客户端的事件。从selector中取出channel(第3步中的那个channel),从通道中读取到数据"HelloServer"。然后向通道写数据"HelloClient"。
7.客户端:第5步中最后负责监听的selector,监听到第6步中服务端的数据,收到"HelloClient"。客户端完成,客户端的selector继续轮询事件。
8.服务端:监听到第6步中自己的写事件,取到channel,取消监听写事件,只监听读事件。

core components

Channel
Overview

Channel can be understood as a channel, through which data can be read and written from different sources (files/networks, etc.) ( channels are based on Buffer for read and write interaction. Because of the Buffer characteristics, channels can read and write asynchronously ), because Channel is Full duplex, so it can map the underlying operating system's APl better than streaming

Scatter && Gather
  • Scattered reading: Scatter the data in the Channel into multiple Buffers (i.e. buffer array) in order
  • Aggregated writing: Aggregate data in multiple Buffers (i.e. buffer arrays) into Channel
Implementation class

  • FileChannel: Mainly used for reading and writing files
  • DatagramChannel: Mainly used for UDP reading and writing data in the network
  • SocketChannel: Read and write data in the network through TCP
  • ServerSocketChannel: Mainly used on the server side, it can monitor new incoming TCP connections, like a web server. A SocketChannel is created for each new incoming connection.
The difference between Channel and Stream
Buffer
Overview

Buffer is an array object, which we can understand as a container that temporarily stores a fixed amount of written or read data. In Java NIO, accessing data in NIO at any time requires operations through the buffer (Buffer). When reading data, read directly from the buffer, and when writing data, write to the buffer.

Classification
  • Indirect buffer: The buffer is allocated in JVM memory through the allocate method
  • Direct buffer: Allocate the buffer in physical memory through the allocateDirect method, which can improve efficiency
ByteBuffer buf = ByteBuffer.allocate(1024); //创建非直接缓冲区大小为1024
ByteBuffer buffer_direct = ByteBuffer.allocateDirect(1024);//创建直接缓冲区大小为1024
System.out.println("判断是否为直接缓冲区 =" + buffer_direct.isDirect()); //true
Subclass
Java's basic types all correspond to a Buffer. They all have the same API. The most commonly used buffer in NIO is ByteBuffer.
core method
  • allocate: allocate a buffer
  • put: store data into the buffer
  • get: Get the data in the buffer
core attributes
  • Capacity: Indicates the maximum capacity of data stored in the buffer. Once declared, it cannot be changed.
  • position: indicates the position of the current operation data in the buffer
    • In write mode, position indicates the current writing position, and the maximum position is capacity-1
    • In read mode, it is the current position of the read data
  • Limit: Indicates the size of data that can be manipulated in the buffer (data cannot be read or written after limit)
    • In write mode, how much data is written and what is the limit?
    • In read mode, indicates how much data can be read
  • 0 <= position <= limit <= capacity ( always unchanged )
    package com.bierce.io;
    import java.nio.ByteBuffer;
    public class TestBuffer{
        public static void main(String[] args) {
            ByteBuffer buf = ByteBuffer.allocate(1024); //创建缓冲区大小为1024
            System.out.println("--------------未写入数据前获取各属性值-------------");
            System.out.println("position = " + buf.position()); // position = 0
            System.out.println("limit = " + buf.limit()); //limit = 1024
            System.out.println("capacity = " + buf.capacity()); //capacity = 1024
            System.out.println("--------------写入数据后获取各属性值--------------");
            String data = "data";
            buf.put(data.getBytes());
            System.out.println("position = " + buf.position()); // position = 4
            System.out.println("limit = " + buf.limit()); //limit = 1024
            System.out.println("capacity = " + buf.capacity()); //capacity = 1024
            System.out.println("--------------切换到读模式下获取各属性值--------------");
            buf.flip();
            buf.put(data.getBytes());
            System.out.println("position = " + buf.position()); // position = 4
            System.out.println("limit = " + buf.limit()); //limit = 4
            System.out.println("capacity = " + buf.capacity()); //capacity = 1024
            System.out.println("--------------切换到写模式下获取各属性值---------------");
            buf.flip();
            System.out.println("position = " + buf.position()); // position = 0
            System.out.println("limit = " + buf.limit()); //limit = 4
            System.out.println("capacity = " + buf.capacity()); //capacity = 1024
        }
    }
    
Selector(selector)

  • The multiplexer Selector is the basis of Java NIO programming. Mastering the Selector proficiently is crucial to mastering NIO programming.
  • The multiplexer provides the ability to select ready tasks, that is, the Selector will continuously poll the Channel registered on it. If there are new TCP connection access, read and write events on a Channel, the Channel is ready. The status will be polled by the Selector, and then the collection of ready channels can be obtained through the SelectionKey for subsequent I/O operations .
  • A multiplexer Selector can poll multiple Channels at the same time. Since the JDK uses epoll instead of the traditional select implementation, it does not have a maximum connection handle limit of 1024/2048. This means that only one thread is required to poll the Selector, and thousands of clients can be involved.
Pipe
Overview

A Java NIO pipe is a one-way data connection between 2 threads. Pipe has a source channel and a sink channel. Data will be written to the sink channel and read from the source channel

Example
Pipe pipe = Pipe.open(); //获取管道
//相当于一个线程写入数据到管道
Pipe.SinkChannel sinkChannel = pipe.sink(); //获取sink管道,用来传送数据
ByteBuffer byteBuffer_write = ByteBuffer.allocate(1024);
byteBuffer_write.put("bierce Never Give up!".getBytes());
byteBuffer_write.flip(); //写入完成后转换为读模式
sinkChannel.write(byteBuffer_write); //通过sink管道发送数据

//相当于另一个线程从管道读取数据
Pipe.SourceChannel sourceChannel = pipe.source(); //获取source管道,用来读取数据
ByteBuffer byteBuffer_read = ByteBuffer.allocate(1024);
int length = sourceChannel.read(byteBuffer_read);
System.out.println(new String(byteBuffer_read.array(), 0, length)); //bierce Never Give up!

//关闭管道资源
sourceChannel.close();
sinkChannel.close();

Applicable scene

It is suitable for architectures with a large number of connections and relatively short connections (light operations), such as chat servers, where concurrency is limited to applications and programming is complicated.

AIO(Asynchronous I/O)

Overview

AIO is also called NIO2, an asynchronous non-blocking mode that appeared after jdk7. The server implementation mode is one effective request and one thread. The client's I/O requests are completed by the OS first and then notify the server application to start the thread for processing. Unlike NIO, when performing read or write operations, you only need to directly call the read or write method of the API. Both methods are asynchronous. For read operations, when there is a stream to read, the operating system will flow the readable stream into the buffer of the read method and notify the application; for write operations, when the operation When the system completes writing the stream passed by the write method, the operating system actively notifies the application . That is to say, it can be understood that the read/write methods are all asynchronous , and the callback function will be actively called after completion ( AIO is not widely used. Netty has also tried to use AIO before, but gave up again )

Applicable scene

JDK7 has begun to support it. It is suitable for architectures with a large number of connections and relatively long connections (heavy operations), such as photo album servers. It fully calls the OS to participate in concurrent operations, and the programming is more complicated.

Guess you like

Origin blog.csdn.net/qq_34020761/article/details/132347187