NIO和AIO

60 阅读2分钟

NIO是同步非阻塞IO,服务器的实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理。用户进程也需要时不时询问IO操作是否就绪,这就要求用户进程不停地去询问。由于所有读写操作都是由acceptor进行分发的,所以效率瓶颈主要在于acceptor。

回忆BIO模型,之所以需要多线程,是因为在进行I/O操作的时候,一是没有办法知道到底能不能写、能不能读,只能”傻等”,即使通过各种估算,算出来操作系统没有能力进行读写,也没法在socket.read()和socket.write()函数中返回,这两个函数无法进行有效的中断。所以除了多开线程另起炉灶,没有好的办法利用CPU。

而NIO的读写函数可以立刻返回,这就给了我们不开线程利用CPU的最好机会:如果一个连接不能读写(socket.read()返回0或者socket.write()返回0),我们可以把这件事记下来,记录的方式通常是在Selector上注册标记位,然后切换到其它就绪的连接(channel)继续进行读写。

AIO是异步非阻塞IO,在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。AIO需要一个连接去注册读写事件和回调方法,也就是说当读写操作进行时,我们只需要调用API中的read和write方法就行了。

缺点:

1、实现复杂。代码量太庞大;

2、需要额外的技能。也就是说你想要学好AIO,还需要java多线程的技术做铺垫才可以。否则我们很难写出质量高的代码。

3、一个著名的Selector空轮询bug。它会导致CPU100%,之前在我的群里面,有人曾经遇到过这个问题,而且官方说在1.6的版本中解决,但是现在还有。遇到的时候我们虽然可以解决但是不知道的人会很痛苦。

4、可靠性差。也就是说我们的网络状态是复杂多样的,会遇到各种各样的问题,比如说网断重连、缓存失效、半包读写等等。可靠性比较差。稍微出现一个问题,还需要大量的代码去完善。