NIO网络编程(九)—— 再探NIO、BIO概念

385 阅读3分钟

这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战

前面在Netty编程(一)—— BIO和NIO - 掘金 (juejin.cn)中介绍了BIO、伪异步IO以及NIO的基本概念,之后的七篇博客介绍了NIO编程的相关知识,这篇博客是NIO网络编程的结尾,讲解一下关于NIO和BIO的概念知识。

Stream与Channel

  • stream 不会自动缓冲数据,channel 会利用系统提供的发送缓冲区、接收缓冲区(更为底层)

  • stream 仅支持阻塞 API,channel 同时支持阻塞、非阻塞 API,网络 channel 可配合 selector 实现多路复用

  • 二者均为全双工,即读写可以同时进行

IO模型

阻塞IO

在这里插入图片描述

用户线程进行read操作时,需要等待操作系统执行实际的read操作,首先要等待数据通过网络到达,然后会复制数据,此期间用户线程是被阻塞的,无法执行其他操作。

非阻塞IO

在这里插入图片描述

  • 用户线程

    在一个循环中一直调用read方法,若内核空间中还没有数据可读,立即返回。但是对于非阻塞IO来说, 只是在等待阶段非阻塞在数据复制阶段还是被阻塞住

  • 用户线程发现内核空间中有数据后,等待内核空间执行复制数据,待复制结束后返回结果

多路复用

在这里插入图片描述

在之前的Java中通过Selector实现多路复用(具体可以看《Netty编程(六)—— nio.Selector之基本使用 - 掘金 (juejin.cn)》 以及 《Netty编程(七)—— nio.Selector之读写事件 - 掘金》 (juejin.cn)

  • 当没有事件是,调用select方法会被阻塞住
  • 一旦有一个或多个事件发生后,就会去处理对应的事件,从而实现多路复用

多路复用与阻塞IO的区别

这里说一下多路复用与阻塞IO的区别

  • 阻塞IO模式下,若线程因accept事件被阻塞,发生read事件后,仍需等待accept事件执行完成后,才能去处理read事件,这也正是阻塞IO的最大问题
  • 多路复用模式下,一个事件发生后,若另一个事件处于阻塞状态,不会影响该事件的执行

异步IO

在这里插入图片描述

所谓异步IO就是线程1调用方法后理解返回,不会被阻塞也不需要立即获取结果当方法的运行结果出来以后,由线程2将结果返回给线程1。

AIO

AIO 是上面讲到的异步IO,他是用来解决数据复制阶段的阻塞问题

  • 同步意味着,在进行读写操作时,线程需要等待结果,还是相当于闲置
  • 异步意味着,在进行读写操作时,线程不必等待结果,而是将来由操作系统来通过回调方式由另外的线程来获得结果

这里可以举一个异步读取文件内容的例子:

@Slf4j
public class TestAIO {
    public static void main(String[] args) throws IOException {
        AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths.get("data2.txt"), StandardOpenOption.READ);
        ByteBuffer buffer = ByteBuffer.allocate(16);
        log.debug("read begin...");
        channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override   //read成功
            public void completed(Integer result, ByteBuffer attachment) {
                log.debug("read complete...{}",result);
                attachment.flip();
                ByteBufferUtil.debugAll(buffer);
            }
            @Override   //read失败
            public void failed(Throwable exc, ByteBuffer attachment) {

            }
        });
        log.debug("read end...");
        System.in.read();
    }
}

结果如下,可以看见读取并打印结果确实是异步执行的

在这里插入图片描述