面试官:关于BIO、NIO、以及AIO有了解吗可以简单聊一聊吗?
面试官心理分析:
主要是想考察io这些基础知识的掌握程度,各种流的使用,
派大星:当然可以的。
- 首先聊一下BIO网络通信原理
最传统的网络通信模型就是BIO,同步阻塞式IO
。通俗的讲就是服务端创建一个ServerSocket,客户端用一个Socket去连接那个Server Socket,ServerSocket接收到一个Socket的连接请求就创建一个Socket和一个线程去和那个Socket进行通信。
客户端和服务端的Socket从而进行同步阻塞式的通信,客户端Socket发送一个请求,服务端Socket进行处理后返回响应,响应不许是等处理完后才返回的。
这种方式最大的缺点就是,每一次客户端接入都需要在服务端创建一个线程来服务这个客服端。如果客户端较多的情况下会导致服务端的线程数量激增。从而导致服务器端程序的负载过高,甚至容易崩溃宕机。
优化的点就是可以创建一个线程池,来固定数量处理客户端的请求。但是如果高并发请求的时候也会导致各种的请求排队和延时。毕竟没有那么多的线程去处理。
- 其次聊一下NIO:
JDK1.4中引入了NIO,这是一种同步非阻塞的IO
。基于Reactor模型
。在NiO中存在一个Buffer
的概念,也就是缓冲区,一般都是将数据写入到Buffer中,然后从Buffer中读取数据。具体的Buffer有:IntBuffer、LongBuffer、CharBuffer、等多种针对于基础类型的Buffer。在NIO还有Channel
的概念。NIO中都是通过Channel来进行数据读写的;还有selector
,这是多路复用器,selector会不断轮询注册的Channel,如果某个Channel上发生了读写事件,selector会将这些Channel获取出来。我们便可通过selector key获取有读写的Channel,就可以进行IO操作。一个selector就通过一个线程便能轮询很多的Channel。这同时也意味着服务端可以接入很多的客户端。
其实具体实现简单的讲就是一个线程处理大量的客户端请求,通过一个线程轮询大量的Channel,每次就获取一批有事件的Channel,然后对每个请求启动一个线程处理即可。实际使用中可以结合CachedThreadPool
线程池。
NIO的核心就是非阻塞的。selector一个线程便可以不停地轮询Channel,所有的客户端请求都不会阻塞,直接就会进来,大不了就是等待一下排队而已。
- 最后说一下AIO:
AIO是基于Proactor
模型的。就是异步非阻塞模型
。简单来讲:每个连接发送过来的请求都会绑定一个Buffer,然后通知操作系统去异步完成读。此时运行的程序是可以处理别的请求的,等操作系统完成数据读取之后就会调用你的接口,将操作系统异步读完的数据给到你,然后便可以对这个数据进行处理并将结果返回。写的时候也是给操作系统一个Buffer,让操作系统自己获取数据去完成写操作,写完之后通知即可。
与BIO不同的是:BIO的工作线程从Channel中读取数据是同步的,是工作线程自己完成的,同时往Channel中写回数据也是工作线程自己完成。这些动作都是同步的。而AIO则是异步的。在AIO中工作线程读取数据的时候,只需要提供一个Buffer给操作系统,然后工作线程即可处理别的事情,读数据的动作交给操作系统,操作系统内核会去完成读取数据并将数据放入到提供的Buffer中。操作系统内核完成这一些列读数据的操作后会回调你的接口通知已经完成并将存好数据的Buffer交给工作线程。写数据同理一样的操作。
面试官:不错,掌握的可以,那你可以说说简单聊聊什么同步阻塞、同步非阻塞、异步非阻塞吗?
派大星:可以的,其实上面已经大致说了一些,下面我在简单阐述一下:
- 为什么BIO叫同步非阻塞呢。其实这个不是针对网络编程模型来说的,是针对文件IO操作来说的,也就是磁盘文件的IO读写(FileInputStream),因为BIO的流读写文件,指的是发起IO的请求直接hang死,必须等着IO处理完成才能返回。
- NIO为什么是同步非阻塞。简单来说就是通过NIO的FileChannel发起的文件IO操作,其实是发起之后就返回了,在这期间可以处理别的事情,这就是非阻塞,只不多接下来你还是需要不断地去轮询操作系统查看IO操作是否完成了。
- AIO之所以是异步非阻塞的,简单来说就是当你通过AIO发起文件IO操作之后会立马返回可以处理别的事情,等到操作系统处理完成后会通过回调告诉你已经处理完成了。不需要你主动轮询(这就是同步)。异步是操作系统主动通知你。
面试官:不错。很好。期待你加入我的团队。