JAVA IO/NIO 什么是异步

225 阅读3分钟

Java IO/NIO

阻塞IO模型

是最传统的IO模型,即在读写数据过程中会发生阻塞现象。当用户线程发出IO请求后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪。而用户线程就会处于阻塞状态,用户线程交出CPU。当数据就绪后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才接触block状态。

典型阻塞IO例子为:

// 如果数据没有就绪,会一直阻塞在read方法
data = socket.read();

非阻塞IO模型

当用户线程发发起一个read操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个error,这就说明数据还没准备好,于是它可以再次发送read操作,直到数据准备好。在非阻塞IO模型中,用户线程需要不断询问内核数据是否就绪,也就是说非阻塞IO一定不会交出CPU,而会一直占用。

典型非阻塞IO例子为:

while(true){
    data = socket.read();
    if(data != error){
        break;
    }
}

在上面的例子我们可以看出,非阻塞IO有一个非常严重的问题,在while循环内不断去询问数据是否可用,这样导致CPU占用率非常高

多路复用IO模型

  • Java IO实际上就是多路复用IO。在多路复用IO中,会有一个线程不断去轮询多个socket的状态,只有当socket真正有读写事件时,才真正调用实际的IO读写操作。只需要使用一个线程就可以管理多个socket,系统不需要建立新的进程或者线程,也不需要维护他们,大大减少了资源占用。因此,复用IO比较适合连接数比较多的情况
  • 在轮询socket状态时,不同于非阻塞IO是通过用户线程询问,多路复用IO是在内核中询问的,这个效率更高
  • 注意:多路复用IO是对到达的事件逐一响应,因此一旦事件响应体很大,那么就会导致后续的事件迟迟得不到处理,并且会影响新的事件轮询。

信号驱动IO

使用信号函数来调用IO独写操作

异步IO模型

  • 最理想的IO模型
  • 当用户线程发起read操作后,立刻可以开始去做其他事情。
  • 从内核角度,当它收到一个asychronous read后,会立刻返回,说明read请求已经发起成功,并不会对用户线程产生任何阻塞。内核会等到数据准备完成,然后将数据拷贝到用户线程
  • 一切完成后,内核会给用户线程发送信号通知read完成
  • 在这个期间,用户线程只需要发起请求,然后收到内核返回的成功信号,就可以直接使用数据了

Java NIO

  • NIO 有三大核心部分:Channel(通道), Buffer(缓冲区),Selector(选择区)。
  • 传统IO基于字节流(InputStream, OutputStream)字符流操作(Reader,Writer)。而NIO基于Channel和Buffer进行操作,数据总是通过通道读取到缓冲区中,或者从缓冲区写入到通道中。选择区用于监听多个通道的事件。
  • IO面向流,NIO面向缓冲区

Channel

通道和Stream(流)类似。区别为流是单向的,而通道是双向的,可以进行读操作和写操作

Buffer

缓冲区实际上是一个容器,是一个连续数组。通道提供从文件,网络读取数据的渠道,但是读取或写入的数据都必须经由Buffer

Selector

  • 选择区是NIO的核心类
  • 选择区能检测多个注册的通道上是否有事件发生,如果有,便获取事件然后针对每个事件进行相应的响应处理
  • 这样单个线程也可以管理多个通道,大大减少了系统开销,也不用维护多个线程。