聊聊BIO、NIO、AIO的区别
举例说明
海底捞很好吃,但是经常要排队。我们就以生活中的这个例子进行讲解。
A顾客去吃海底捞,就这样干坐着等了一小时,然后才开始吃火锅。(BIO)
B顾客去吃海底捞,他一看要等挺久,于是去逛商场,每次逛一会就跑回来看有没有排到他。于是他最后既购了物,又吃上海底捞了。(NIO)
C顾客去吃海底捞,由于他是高级会员,所以店长说,你去商场随便玩吧,等下有位置,我立马打电话给你。于是C顾客不用干坐着等,也不用每过一会儿就跑回来看有没有等到,最后也吃上了海底捞(AIO)
BIO(Blocking IO)
- 早期传统的IO模型(JDK1.4之前),同步阻塞模式
- 线程发起IO请求后,一直阻塞IO,直到缓冲区数据就绪后再进入下一步操作
- 网络间通信是一请求一应答方式的,若每个请求都需要新建一个线程来专门处理,那么在高并发场景下,机器资源很快就会被耗尽
NIO (New IO)
NIO(Non-Blocking IO)
- 同步非阻塞模式
- 线程发起IO请求后,立即返回
- 非阻塞模式下,用户线程不必原地等待IO缓冲区,可以去做其他操作
- 它需要定时轮询检查IO缓冲区数据是否就绪
- NIO+IO多路复用技术
- 普通的NIO是线程轮询查看一个IO缓冲区是否就绪,而Java中的new IO指的是线程轮询地去查看一堆IO缓冲区中哪些就绪(多路复用思想)
- IO多路复用模型中,将检查IO数据是否就绪的任务,交给系统级别的select/epoll模型,由系统进行监控,减轻用户线程负担
- NIO主要由buffer、channel、selector三种技术整合
- 通过零拷贝的buffer取得数据,每个客户端通过channel在select(多路复用器)上进行注册。
- 服务端不断轮询channel来获取客户端的信息
- channel上的四种状态标识,根据标识进行后续操作
- connect
- accept(阻塞)
- read
- write
- 因此一个服务端可以接收无限多的channel,不需要新开一个线程,进而大大提升性能
AIO
- AIO是真正意义上的异步非阻塞IO模型
- 上述NIO实现中,需要用户线程定时轮询,去检查IO缓冲区数据是否就绪,占用应用程序线程资源,其实轮询相当于还是阻塞的,并非真正解放当前线程,因为它还是需要去查询哪些IO就绪。而真正的理想的异步非阻塞IO应该让内核系统完成,用户线程只需要告诉内核,当缓冲区就绪后,通知我或者执行我交给你的回调函数
- AIO可以做到真正的异步的操作,但实现起来比较复杂,支持纯异步IO的操作系统非常少,目前也就windows是IOCP技术实现了,而在Linux上,底层还是是使用的epoll实现的
总结
并发连接数不多时采用BIO,因为它编程和调试都非常简单,但如果涉及到高并发的情况,应选择NIO或AIO,更好的建议是采用成熟的网络通信框架Netty
BIO | NIO | AIO | |
---|---|---|---|
是否阻塞 | 阻塞 | 非阻塞 | 非阻塞 |
同步/异步 | 同步 | 同步 | 异步 |
性能 | 低 | 一般 | 高 |
客户端与线程比 | 1:1 | N:1 | N:0 |
吞吐量 | 低 | 高 | 高 |
可靠性 | 非常差 | 高 | 高 |
调试难度 | 简单 | 复杂 | 复杂 |
使用场景 | 连接数较小且固定架构 | 连接数多且连接较短(轻操作架构),如聊天服务器 | 连接数多且连接较长(重操作架构),如相册服务器 |