
1. 阻塞IO(blocking IO)
- 应用程序调用一个IO函数,导致应用程序阻塞,如果数据已经准备好,从内核拷贝到用户空间,否则一直等待下去。一个典型的读操作流程大致如下图,当用户进程调用recvfrom这个系统调用时,kernel就开始了IO的第一个阶段:准备数据,就是数据被拷贝到内核缓冲区中的一个过程(很多网络IO数据不会那么快到达,如没收一个完整的UDP包),等数据到操作系统内核缓冲区了,就到了第二阶段:将数据从内核缓冲区拷贝到用户内存,然后kernel返回结果,用户进程才会解除block状态,重新运行起来
blocking IO的特点就是在IO执行的两个阶段用户进程都会block住

2. 非阻塞IO(nonblocking IO)
- 非阻塞IO模型,我们把一个套接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断地测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间
- 当用户的进程发出read操作时,如果kernel中数据还没有准备好,那么并不会block用户进程,而是立即返回error,用户进程判断结果是error,就知道数据还没有准备好,用户可以再次发read,直到kernel中数据准备好,并且用户再一次发read操作,产生system call, 那么kernel马上将数据拷贝到用户内存,然后返回; 所以nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有
- 阻塞IO一个线程只能处理一个IO流事件,要想同时处理多个IO流事件要么多线程要么多进程,这样做效率显然不会高,而非阻塞IO可以一个线程处理多个流事件,只要不停地查询所有流事件即可,当然这个方式也不好,当大多数流没数据时,也是会大量浪费CPU资源;为了避免CPU空转,引进代理(select和poll,两种方式相差不大),代理可以观察多个流I/O事件,空闲时会把当前线程阻塞掉,当有一个或多个I/O事件时,就从阻塞态醒过来,把所有IO流都轮询一遍,于是没有IO时间我们的程序就阻塞在select方法处,即使这样依然存在问题,我们从select处只是知道有IO事件发生,却不知道是哪几个流,还是只能轮询所有流,epoll这样的代理就可以把哪个流发生怎样的IO事件通知我们

3. I/O多路复用模型(IO multiplexing)
- I/O多路复用就在于单个进程可以同时处理多个网络连接IO,基本原理就是select,poll,epoll这些个函数会不断轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程,这三个function会阻塞进程,但和IO阻塞不同,这些函数可以同时阻塞多个IO操作,而且可以同时对多个读操作、写操作IO进行检验,直到有数据到达,才真正调用IO操作函数,调用过程如下图;所以IO多路复用的特点是通过一种机制一种进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中任意一个进入就绪状态,select函数就可以返回
- IO多路复用的优势在于并发数比较高的IO操作情况,可以同时处理多个连接,和blocking IO一样socket是被阻塞的,只不过在多路复用中socket是被select阻塞,而在阻塞IO中是被socket IO给阻塞

4. 信号驱动I/O模型
- 可以用信号,让内核在描述符就绪时发送SIGIO信号通知我们,通过sigaction系统调用安装一个信号处理函数。该系统调用将立即返回,我们的进程继续工作,也就是说它并没有被阻塞。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已经准备好待处理。特点:等待数据报到达期间进程不被阻塞。主循环可以继续执行,只要等待来自信号处理函数的通知:既可以是数据已准备好被处理,也可以是数据报已准备好被读取

5. 异步I/O(asynchronous IO)
-
异步IO告知内核启动某个操作,并让内核在整个操作(包括将内核数据复制到我们自己的缓冲区)完成后通知我们,调用aio_read(Posix异步I/O函数以aio或lio开头)函数,给内核传递描述字、缓冲区指针、缓冲区大小(与read相同的三个参数)、文件偏移以及通知的方式,然后系统立即返回。我们的进程步阻塞等待I/O操作的完成。当内核将数据拷贝到缓冲区后,再通知应用程序
-
用户进程发起read操作之后,立刻就可以开始去做其它的事情。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了

6. NIO和IO的区别
- NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO
| IO | NIO |
|---|---|
| 面向流 | 面向缓冲 |
| 阻塞IO | 非阻塞IO |
| 无 | 选择器 |
7. NIO和IO适用场景
- NIO是为弥补传统IO的不足而诞生的,但是NIO也有缺点,因为NIO是面向缓冲区的操作,每一次的数据处理都是对缓冲区进行的,那么就会有一个问题,在数据处理之前必须要判断缓冲区的数据是否完整或者已经读取完毕,如果没有,假设数据只读取了一部分,那么对不完整的数据处理没有任何意义。所以每次数据处理之前都要检测缓冲区数据
- NIO和IO适用场景:
- 如果需要管理同时打开的成千上万个连接这些连接每次只是发送少量的数据,例如聊天服务器,这时候用NIO处理数据可能是个很好的选择
- 如果只有少量的连接,而这些连接每次要发送大量的数据,这时候传统的IO更合适。使用哪种处理数据,需要在数据的响应等待时间和检查缓冲区数据的时间上作比较来权衡选择
8. NIO核心组件
- channel、buffer、selector
9. 什么是channel?
- 一个Channel(通道)代表和某一实体的连接,这个实体可以是文件、网络套接字等。也就是说,通道是Java NIO提供的一座桥梁,用于我们的程序和操作系统底层I/O服务进行交互
- 通道是一种很基本很抽象的描述,和不同的I/O服务交互,执行不同的I/O操作,实现不一样,因此具体的有FileChannel、SocketChannel等
- 通道使用起来和Stream比较像,可以读取数据到Buffer中,也可以把Buffer中的数据写入通道

- 当然,也有区别,主要体现在以下两点:
- 一个通道,即可以读又可以写,而一个Stream是单向的(所以分InputStream和OutputStream)
- 通道有非阻塞I/O模式
10. Java NIO中最常用的通道实现?
- FileChannel:读取文件
- DatagramChannel:UDP协议网络通信
- SocketChannel:TCP协议网络通信
- ServerSocketChannel:监听TCP连接
上一篇:Java面试手册V2.0+突击V3.0知识点整理(十一) 附封面图片😍
下一篇:Java面试手册V2.0+突击V3.0知识点整理(十三) 附封面图片😍
如果觉得封面图片还行,请顺手点个赞😍

本文探讨了阻塞IO、非阻塞IO、I/O多路复用、信号驱动IO及异步IO的区别与优势,重点讲解了NIO与IO的对比,适合场景及NIO核心组件。涵盖了Channel、Buffer和Selector的应用。
181

被折叠的 条评论
为什么被折叠?



