这是我参与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();
}
}
复制代码
结果如下,可以看见读取并打印结果确实是异步执行的