java-nio

5种 io模型

阻塞IO、非阻塞IO、多路复用IO、信号驱动IO、异步IO

前四个都是同步IO,在内核数据copy到用户程序时都是阻塞的,而第五个则是异步的

NIO即 Non-Block IO, 使用多路复用模型实现 单线程 多通道。

I/O 多路复用主要由以下实现

  • select 它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符,将数据从kernel复制到进程缓冲区。select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select单个进程能够监视的文件描述符的数量存在最大限制,一般为1024(64位为 2048)。随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。

  • poll 它和select在本质上没有多大差别,但poll使用链表来存储文件描述符,没有数量限制。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

  • epoll epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。
select poll epoll
操作方式 遍历 遍历 回调
底层实现 数组 链表 哈希表
IO效率 每次调用都进行线性遍历,时间复杂度为O(n) 每次调用都进行线性遍历,时间复杂度为O(n) 事件通知方式,每当fd就绪,系统注册的回调函数就会被调用,将就绪fd放到readyList里面,时间复杂度O(1)
最大连接数 1024(x86)或2048(x64) 无上限 无上限
fd拷贝 每次调用select,都需要把fd集合从用户态拷贝到内核态 每次调用poll,都需要把fd集合从用户态拷贝到内核态 调用epoll_ctl时拷贝进内核并保存,之后每次epoll_wait不拷贝

在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点:

1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。

Reference

https://github.com/CyC2018/CS-Notes/blob/master/notes/Java%20IO.md