典型 Java 服务端处理网络请求的 IO 过程:
应用程序再用户态,不能直接访问内核空间,若想进行如文件管理、进程通信、内存管理、外界客户端通信等 IO 操作时,需要发起 I/O 调用,由操作系统的内核代为完成
-
操作系统通过网卡,读取客户端的请求数据到内核缓冲区。
-
内核再将数据从内核缓冲区拷贝到 Java 进程缓冲区。
- 内核等待 I/O 设备准备好数据
- 内核将数据从内核空间的拷贝到用户空间。
-
Java 进程处理完请求数据后把构建好的响应从用户缓冲区写入内核缓冲区。
-
内核再通过网络 I/O ,将内核缓冲区中的数据,写入网卡,网卡通过底层的通讯协议,将响应数据发送给目标客户端
BIO 同步阻塞模型
BIO:blocking-IO
- 应用进程发起系统调用后,会一直阻塞,直到内核把数据拷贝到用户空间
- 缺点:线程阻塞,浪费性能,只适用于连接数量少时,当面对十万甚至百万级连接时就不适用
non-blocking-IO 同步非阻塞模型
- 应用进程发起系统调用后,内核中若数据还没准备好,则返回一个 error 使进程不至于等待阻塞,进程可以去做其它事,也可以再次发起系统调用,以此不断发起调用,直到内核数据准备好了开始拷贝数据,拷贝数据期间进程还是阻塞的
- 缺点:频繁的系统调用,同样会消耗大量的CPU资源
NIO 同步多路复用模型
- 线程与 Selector配合,通过 Selector的 select来实现对多个 Channel 可读写事件的监控, 当内核把数据准备好后,select会通知线程,线程再发起 read 调用,拷贝数据期间进程还是阻塞的
- 优点:多路复用模型,通过减少无效的系统调用,减少了对 CPU 资源的消耗
AIO 异步非阻塞模型
AIO:asynchronous-non-blocking-IO
AIO 也就是 NIO 2。Java 7 中引入了 NIO 的改进版 NIO 2,它是异步 IO 模型
- 用户进程发起read系统调用后,内核会立刻返回回应,进程不用等待关注,继续做其它事,不会阻塞进程。内核等待数据准备完成并将拷贝到用户内存,拷贝完成后会有另一个线程****给通知用户进程,告诉它操作完成了,进程全程不阻塞
AIO 用来解决数据复制阶段的阻塞问题
- 同步意味着,在进行读写操作时,线程需要等待结果,还是相当于闲置
- 异步意味着,在进行读写操作时,线程不必等待结果,而是将来由操作系统来通过回调方式由另外的线程来获得结果
- 同步:线程自己去获取结果(一个线程)
- 异步:线程自己不去获取结果,而是由其它线程送结果(至少两个线程)
异步模型需要底层操作系统(Kernel)提供支持
- Windows 系统通过 IOCP 实现了真正的异步 IO
- Linux 系统异步 IO 在 2.6 版本引入,但其底层实现还是用多路复用模拟了异步 IO,性能没有优势