Flujo IO de Java (2) Modelo IO (BIO | NIO | AIO)

Descripción general

El modelo Java IO incluye IO de bloqueo síncrono (BIO), IO sin bloqueo síncrono (NIO) y IO sin bloqueo asíncrono (AIO/NIO2). BIO, NIO y AIO en Java se entienden como los diversos modelos IO de Sistema operativo implementado mediante el lenguaje Java.

modelo IO

BIO (bloqueo de E/S)

Descripción general

BIO es un modo de sincronización y bloqueo. El modo de implementación del servidor es una conexión y un hilo. Es decir, cuando el cliente tiene una solicitud de conexión, el servidor necesita iniciar un hilo para procesar. Si la conexión no hace nada, hilo innecesario Se producirá una sobrecarga. Por supuesto, se puede mejorar a través del mecanismo del grupo de subprocesos, pero las deficiencias esenciales no se han mejorado ( flujo Java IO (1) Los códigos de práctica básicos de IO se basan todos en BIO )

Escena aplicable

Es adecuado para arquitecturas fijas con un número relativamente pequeño de conexiones ( menos de 1000 por máquina ). Este método tiene requisitos relativamente altos para los recursos del servidor y la concurrencia se limita a las aplicaciones. Era la única opción antes de JDK1.4, pero el El programa es intuitivo, sencillo y fácil de entender.

NIO (Nueva E/S)

Descripción general

NIO es un modo sincrónico sin bloqueo. Es una nueva API IO introducida desde JDK1.4. Puede reemplazar el BIO estándar. NIO admite operaciones de IO basadas en canales y orientadas al búfer (el canal es responsable de la transmisión y el búfer es responsable de almacenamiento ) Leer y escribir archivos de una manera más eficiente

Proceso de implementación

# 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,取消监听写事件,只监听读事件。

componentes centrales

Canal
Descripción general

El canal puede entenderse como un canal a través del cual se pueden leer y escribir datos desde diferentes fuentes (archivos/redes, etc.) (los canales se basan en Buffer para la interacción de lectura y escritura. Debido a las características del Buffer, los canales pueden leer y escribir asincrónicamente ), porque el canal es dúplex completo, por lo que puede asignar la APL del sistema operativo subyacente mejor que la transmisión por secuencias.

Dispersar y reunir
  • Lectura dispersa: distribuye los datos del canal en varios búferes (es decir, una matriz de búfer) en orden.
  • Escritura agregada: agregue datos en múltiples búferes (es decir, matrices de búfer) en el canal
Clase de implementación

  • FileChannel: utilizado principalmente para leer y escribir archivos
  • DatagramChannel: se utiliza principalmente para leer y escribir datos UDP en la red.
  • SocketChannel: lee y escribe datos en la red a través de TCP
  • ServerSocketChannel: utilizado principalmente en el lado del servidor, puede monitorear nuevas conexiones TCP entrantes, como un servidor web. Se crea un SocketChannel para cada nueva conexión entrante.
La diferencia entre canal y streaming
Buffer
Descripción general

Buffer es un objeto de matriz, que podemos entender como un contenedor que almacena temporalmente una cantidad fija de datos escritos o leídos. En Java NIO, acceder a los datos en NIO en cualquier momento requiere operaciones a través del buffer (Buffer). Al leer datos, lea directamente desde el búfer y cuando escriba datos, escriba en el búfer.

Clasificación
  • Búfer indirecto: el búfer se asigna en la memoria JVM mediante el método de asignación
  • Búfer directo: asigne el búfer en la memoria física mediante el método allocateDirect, que puede mejorar la eficiencia.
ByteBuffer buf = ByteBuffer.allocate(1024); //创建非直接缓冲区大小为1024
ByteBuffer buffer_direct = ByteBuffer.allocateDirect(1024);//创建直接缓冲区大小为1024
System.out.println("判断是否为直接缓冲区 =" + buffer_direct.isDirect()); //true
Subclase
Todos los tipos básicos de Java corresponden a un búfer. Todos tienen la misma API. El búfer más utilizado en NIO es ByteBuffer.
métodos básicos
  • asignar: asignar un búfer
  • poner: almacenar datos en el buffer
  • get: obtiene los datos en el buffer
atributos centrales
  • Capacidad: Indica la capacidad máxima de datos almacenados en el buffer, una vez declarada no se puede cambiar.
  • posición: indica la posición de los datos de la operación actual en el búfer
    • En modo de escritura, la posición indica la posición de escritura actual y la posición máxima es capacidad-1
    • En modo lectura, es la posición actual de los datos leídos.
  • Límite: indica el tamaño de los datos que se pueden manipular en el búfer (los datos no se pueden leer ni escribir después del límite)
    • En modo escritura, ¿cuántos datos se escriben y cuál es el límite?
    • En modo lectura, indica cuántos datos se pueden leer.
  • 0 <= posición <= límite <= capacidad ( siempre sin cambios )
    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)

  • El selector multiplexor es la base de la programación Java NIO. Dominar el selector con fluidez es crucial para dominar la programación NIO.
  • El multiplexor proporciona la capacidad de seleccionar tareas listas, es decir, el selector sondeará continuamente el canal registrado en él. Si hay nuevos accesos a conexiones TCP, eventos de lectura y escritura en un canal, el canal está listo. Se sondeará el estado por el Selector, y luego la colección de canales listos se puede obtener a través de SelectionKey para operaciones de E/S posteriores .
  • Un selector multiplexor puede sondear varios canales al mismo tiempo. Dado que el JDK utiliza epoll en lugar de la implementación de selección tradicional, no tiene un límite máximo de manejo de conexión de 1024/2048. Esto significa que solo se requiere un hilo para sondear el Selector y pueden participar miles de clientes.
Tubo
Descripción general

Una tubería Java NIO es una conexión de datos unidireccional entre 2 subprocesos. La tubería tiene un canal fuente y un canal sumidero. Los datos se escribirán en el canal receptor y se leerán desde el canal fuente.

Ejemplo
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();

Escena aplicable

Es adecuado para arquitecturas con una gran cantidad de conexiones y conexiones relativamente cortas (operaciones livianas), como servidores de chat, donde la concurrencia se limita a las aplicaciones y la programación es complicada.

AIO (E/S asíncrona)

Descripción general

AIO también se llama NIO2, un modo asíncrono sin bloqueo que apareció después de jdk7. El modo de implementación del servidor es una solicitud efectiva y un hilo. Las solicitudes de E/S del cliente son completadas por el sistema operativo primero y luego notifica a la aplicación del servidor para iniciar el hilo para su procesamiento. A diferencia de NIO, al realizar operaciones de lectura o escritura, solo necesita llamar directamente al método de lectura o escritura de la API. Ambos métodos son asincrónicos. Para operaciones de lectura, cuando hay una secuencia para leer, el sistema operativo hará fluir la secuencia legible al búfer del método de lectura y notificará a la aplicación; para operaciones de escritura, cuando la operación Cuando el sistema complete la escritura flujo pasado por el método de escritura, el sistema operativo notifica activamente a la aplicación . Es decir, se puede entender que todos los métodos de lectura y escritura son asincrónicos y la función de devolución de llamada se llamará activamente una vez completada ( AIO no se usa ampliamente. Netty también intentó usar AIO antes, pero se rindió nuevamente ).

Escena aplicable

JDK7 ha comenzado a admitirlo. Es adecuado para arquitecturas con una gran cantidad de conexiones y conexiones relativamente largas (operaciones pesadas), como servidores de álbumes de fotos. Llama completamente al sistema operativo para participar en operaciones concurrentes y la programación es más complicada. .

Supongo que te gusta

Origin blog.csdn.net/qq_34020761/article/details/132347187
Recomendado
Clasificación