面试题答案
一键面试1. Netty 中常用的IO模型
- BIO(Blocking I/O,阻塞式I/O):
- 工作方式:在传统BIO模型中,一个客户端连接对应一个服务器端线程。当客户端发起连接请求,服务器端线程在处理该连接的输入输出操作时会处于阻塞状态,直到数据传输完成。例如,在一个简单的Socket通信中,
ServerSocket
监听端口,每当有新连接进来,就会创建一个新的Thread
来处理这个连接的读写操作。在read
方法调用时,如果没有数据可读,线程就会一直阻塞等待。 - 缺点:这种模型在高并发场景下性能较差,因为线程资源是有限的,大量客户端连接会导致线程数量剧增,从而消耗大量系统资源,并且线程上下文切换也会带来额外开销。
- 工作方式:在传统BIO模型中,一个客户端连接对应一个服务器端线程。当客户端发起连接请求,服务器端线程在处理该连接的输入输出操作时会处于阻塞状态,直到数据传输完成。例如,在一个简单的Socket通信中,
- NIO(Non - Blocking I/O,非阻塞式I/O):
- 工作方式:NIO是基于缓冲区(Buffer)和通道(Channel)进行操作的。通道可以异步地读写数据,一个线程可以管理多个通道。例如,
ServerSocketChannel
可以监听新的连接,SocketChannel
用于数据传输。在NIO中,read
和write
操作不会阻塞线程,如果没有数据可读或可写,这些方法会立即返回,线程可以继续执行其他任务。同时,NIO引入了选择器(Selector),它可以管理多个通道,通过select
方法阻塞等待感兴趣的事件(如连接就绪、读就绪、写就绪等)发生。 - 优点:相比BIO,NIO更适合高并发场景,通过减少线程数量,降低了系统资源的消耗,提高了系统的并发处理能力。
- 工作方式:NIO是基于缓冲区(Buffer)和通道(Channel)进行操作的。通道可以异步地读写数据,一个线程可以管理多个通道。例如,
- AIO(Asynchronous I/O,异步I/O):
- 工作方式:AIO也叫NIO.2,是异步非阻塞的I/O模型。与NIO不同的是,AIO的读写操作完全是异步的。当发起一个I/O操作时,调用者可以立即返回,无需像NIO那样轮询检查操作是否完成。操作系统会在I/O操作完成后通知应用程序。例如,在AIO中,
AsynchronousSocketChannel
的read
操作可以传入一个Future<Integer>
或者一个CompletionHandler
,当I/O操作完成时,Future
会得到结果,或者CompletionHandler
的回调方法会被调用。 - 优点:AIO进一步提升了系统在高并发场景下的性能,真正实现了异步操作,应用程序可以更加专注于业务逻辑,而无需关心I/O操作的状态。
- 工作方式:AIO也叫NIO.2,是异步非阻塞的I/O模型。与NIO不同的是,AIO的读写操作完全是异步的。当发起一个I/O操作时,调用者可以立即返回,无需像NIO那样轮询检查操作是否完成。操作系统会在I/O操作完成后通知应用程序。例如,在AIO中,
2. 从内核角度看NIO多路复用的实现
- 多路复用技术:NIO中的多路复用主要通过选择器(Selector)实现,在Linux内核层面,常见的多路复用系统调用有
select
、poll
和epoll
。select
:它通过一个fd_set
结构体来管理一组文件描述符(对应NIO中的通道),select
函数会阻塞等待其中任何一个文件描述符上有感兴趣的事件发生。当select
返回时,应用程序需要遍历整个fd_set
来找出哪些文件描述符上发生了事件。这种方式的缺点是fd_set
有最大文件描述符数量限制(通常是1024),并且每次遍历都需要消耗一定的时间,在高并发场景下效率较低。poll
:与select
类似,但它使用pollfd
结构体数组来管理文件描述符,没有了文件描述符数量的限制。不过,它同样需要在返回时遍历整个数组来确定发生事件的文件描述符,在高并发下性能依然不够理想。epoll
:这是Linux 2.6内核引入的高效多路复用机制。它通过epoll_create
创建一个epoll
实例,通过epoll_ctl
向这个实例中添加、修改或删除需要监听的文件描述符及其感兴趣的事件。epoll_wait
函数用于阻塞等待事件发生。与select
和poll
不同的是,epoll
使用事件驱动机制,当有事件发生时,内核会将发生事件的文件描述符直接返回给应用程序,无需遍历所有文件描述符,大大提高了效率,非常适合高并发场景。在NIO中,Selector
在Linux系统上底层默认使用epoll
实现(如果操作系统支持),从而实现高效的多路复用。