1、Stream 与 Channel

2、IO 模型

当调用一次 channel.read 或 stream.read 后,会由用户态切换至操作系统内核态来完成真正数据读取,而读取又分为两个阶段,分别为:

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~
根据 UNIX 网络编程 - 卷 I,IO 模型主要有以下几种

阻塞 IO

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~

非阻塞IO

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~

多路复用

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~

Java 中通过 Selector 实现多路复用

多路复用与阻塞IO的区别

异步IO

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~

3、零拷贝

零拷贝指的是数据无需拷贝到 JVM 内存中,同时具有以下三个优点

传统 IO 问题

传统的 IO 将一个文件通过 socket 写出

File f = new File("helloword/data.txt");
RandomAccessFile file = new RandomAccessFile(file, "r");
byte[] buf = new byte[(int)f.length()];
file.read(buf);
Socket socket = ...;
socket.getOutputStream().write(buf);

内部工作流如下

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~

可以看到中间环节较多,java 的 IO 实际不是物理设备级别的读写,而是缓存的复制,底层的真正读写是操作系统来完成的

NIO优化

通过 DirectByteBuf

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~
大部分步骤与优化前相同,唯有一点:Java 可以使用 DirectByteBuffer 将堆外内存映射到 JVM 内存中来直接访问使用

进一步优化 1

以下两种方式都是零拷贝,即无需将数据拷贝到用户缓冲区中(JVM 内存中)

底层采用了 linux 2.1 后提供的 sendFile 方法,Java 中对应着两个 channel 调用 transferTo/transferFrom 方法拷贝数据

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~

这种方法下

进一步优化 2

linux 2.4 对上述方法再次进行了优化

IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~

整个过程仅只发生了 1 次用户态与内核态的切换,数据拷贝了 2 次

4、AIO

AIO 用来解决数据复制阶段的阻塞问题

异步模型需要底层操作系统(Kernel)提供支持

  • Windows 系统通过 IOCP 实现了真正的异步 IO
  • Linux 系统异步 IO 在 2.6 版本引入,但其底层实现还是用多路复用模拟了异步 IO,性能没有优势

本文由传智教育博学谷教研团队发布。

如果本文对您有帮助,欢迎关注点赞;如果您有任何建议也可留言评论私信,您的支持是我坚持创作的动力。

转载请注明出处!

发表回复