2.8.1. 阻塞 IO 模型 异步
最传统的一种 IO 模型,即在读写数据过程当中会发生阻塞现象。当用户线程发出 IO 请求以后,内 核会去查看数据是否就绪,若是没有就绪就会等待数据就绪,而用户线程就会处于阻塞状态,用 户线程交出 CPU。当数据就绪以后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用 13/04/2018 Page 35 of 283 户线程才解除 block 状态。典型的阻塞 IO 模型的例子为:data = socket.read();若是数据没有就 绪,就会一直阻塞在 read 方法。socket
2.8.2. 非阻塞 IO 模型 async
当用户线程发起一个 read 操做后,并不须要等待,而是立刻就获得了一个结果。若是结果是一个 error 时,它就知道数据尚未准备好,因而它能够再次发送 read 操做。一旦内核中的数据准备 好了,而且又再次收到了用户线程的请求,那么它立刻就将数据拷贝到了用户线程,而后返回。 因此事实上,在非阻塞 IO 模型中,用户线程须要不断地询问内核数据是否就绪,也就说非阻塞 IO 不会交出 CPU,而会一直占用 CPU。典型的非阻塞 IO 模型通常以下:函数
while(true){ data = socket.read(); if(data!= error){ 处理数据 break; } }操作系统
可是对于非阻塞 IO 就有一个很是严重的问题,在 while 循环中须要不断地去询问内核数据是否就 绪,这样会致使 CPU 占用率很是高,所以通常状况下不多使用 while 循环这种方式来读取数据。线程
2.8.3. 多路复用 IO 模型 进程
多路复用 IO 模型是目前使用得比较多的模型。Java NIO 实际上就是多路复用 IO。在多路复用 IO 模型中,会有一个线程不断去轮询多个 socket 的状态,只有当 socket 真正有读写事件时,才真 正调用实际的 IO 读写操做。由于在多路复用 IO 模型中,只须要使用一个线程就能够管理多个 socket,系统不须要创建新的进程或者线程,也没必要维护这些线程和进程,而且只有在真正有 socket 读写事件进行时,才会使用 IO 资源,因此它大大减小了资源占用。在 Java NIO 中,是通 过 selector.select()去查询每一个通道是否有到达事件,若是没有事件,则一直阻塞在那里,所以这 种方式会致使用户线程的阻塞。多路复用 IO 模式,经过一个线程就能够管理多个 socket,只有当 socket 真正有读写事件发生才会占用资源来进行实际的读写操做。所以,多路复用 IO 比较适合连 接数比较多的状况。 另外多路复用 IO 为什么比非阻塞 IO 模型的效率高是由于在非阻塞 IO 中,不断地询问 socket 状态 时经过用户线程去进行的,而在多路复用 IO 中,轮询每一个 socket 状态是内核在进行的,这个效 率要比用户线程要高的多。 不过要注意的是,多路复用 IO 模型是经过轮询的方式来检测是否有事件到达,而且对到达的事件 逐一进行响应。所以对于多路复用 IO 模型来讲,一旦事件响应体很大,那么就会致使后续的事件 迟迟得不处处理,而且会影响新的事件轮询。 事件
2.8.4. 信号驱动 IO 模型 资源
在信号驱动 IO 模型中,当用户线程发起一个 IO 请求操做,会给对应的 socket 注册一个信号函 数,而后用户线程会继续执行,当内核数据就绪时会发送一个信号给用户线程,用户线程接收到 信号以后,便在信号函数中调用 IO 读写操做来进行实际的 IO 请求操做。效率
2.8.5. 异步 IO 模型
异步 IO 模型才是最理想的 IO 模型,在异步 IO 模型中,当用户线程发起 read 操做以后,马上就 能够开始去作其它的事。而另外一方面,从内核的角度,当它受到一个 asynchronous read 以后, 它会马上返回,说明 read 请求已经成功发起了,所以不会对用户线程产生任何 block。而后,内 核会等待数据准备完成,而后将数据拷贝到用户线程,当这一切都完成以后,内核会给用户线程 发送一个信号,告诉它 read 操做完成了。也就说用户线程彻底不须要实际的整个 IO 操做是如何 进行的,只须要先发起一个请求,当接收内核返回的成功信号时表示 IO 操做已经完成,能够直接 去使用数据了。 也就说在异步 IO 模型中,IO 操做的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完 成,而后发送一个信号告知用户线程操做已完成。用户线程中不须要再次调用 IO 函数进行具体的 读写。这点是和信号驱动模型有所不一样的,在信号驱动模型中,当用户线程接收到信号表示数据 已经就绪,而后须要用户线程调用 IO 函数进行实际的读写操做;而在异步 IO 模型中,收到信号 表示 IO 操做已经完成,不须要再在用户线程中调用 IO 函数进行实际的读写操做。
注意,异步 IO 是须要操做系统的底层支持,在 Java 7 中,提供了 Asynchronous IO。