输入输出(IO)是指计算机同任何外部设备之间的数据传递。常见的输入输出设备有文件、键盘、打印机、屏幕等。数据可以按记录(或称数据块)的方式传递,也可以用流的方式传递。
在socket编程中,需要读取和输出数据,这就用到了IO模型。Java在1.4之前用到的是同步阻塞的方式,性能存在瓶颈,后来又出现了非阻塞io,再后来1.7之后,又出现了AIO,总体来说这三类IO模型。

几种概念

同步和异步

  • 同步
    概念:发送一个IO请求,等待着IO结果的返回,在此期间不能再发请求。
  • 异步
    概念:发送一个IO请求后,不需要等待,可以随时发送请求或者做其他事情,io完成后通知用户进程。

IO操作主要分为两个步骤,即发起IO请求和实际IO操作,同步与异步的区别就在于第二个步骤是否阻塞请求进程。若实际IO操作阻塞请求进程,即请求进程需要等待或者轮询查看IO操作是否就绪,则为同步IO;若实 IO操作并不阻塞请求进程,而是由操作系统来进行实际IO操作并将结果返回,则为异步IO。

阻塞和非阻塞

  • 阻塞
    概念:一个线程调用read()或者write()方法时,该线程将被阻塞,直到有一些数据读读取或者被写入,在此期间,该线程不能执行其他任何任务。在完成网络通信进行IO操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量的客户端时,性能急剧下降。
  • 非阻塞
    概念:当线程从某通道进行读写数据时,若没有数据可用时,该线程会去执行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理多个输入和输出通道。因此NIO可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。

IO操作主要分为两个步骤,即发起IO请求和实际IO操作,阻塞与非阻塞的区别就在于第一个步骤是否阻塞。若发起IO请求后请求线程一直等待实际IO操作完成,则为阻塞IO;若发起IO请求后请求线程返回而不会一直等待,即为非阻塞IO。

IO模型的分类

主要分为以下几类:

  • 同步(synchronous) IO和异步(asynchronous) IO
  • 阻塞(blocking) IO和非阻塞(non-blocking)IO
  • 同步阻塞(blocking-IO)简称BIO
  • 同步非阻塞(non-blocking-IO)简称NIO
  • 异步非阻塞(synchronous-non-blocking-IO)简称AIO

BIO

同步阻塞IO,一个连接开启一个线程,等待IO期间什么也不做,这种IO造成了不必要的资源浪费。

NIO

同步非阻塞IO,客户端发送的请求统一注册到多路复用器上,多路复用器轮询,有io请求时则启动一个线程去处理,用户进程要轮询io操作是否就绪。

NIO分为:Channel(通道),Buffer(缓冲区),Selector(选择器)三个类。
Buffer是一个对象。它包含一些要写入或者读出的数据。在面向流的I/O中,可以将数据写入或者将数据直接读到Stream对象中。缓冲区实质是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息。
Channel是一个通道,可以通过它读取和写入数据,他就像自来水管一样,网络数据通过Channel读取和写入。
通道和流不同之处在于通道是双向的,流只是在一个方向移动,而且通道可以用于读,写或者同时用于读写。
Selector选择器可以监听多个Channel通道感兴趣的事情(read、write、accept(服务端接收)、connect,实现一个线程管理多个Channel,节省线程切换上下文的资源消耗。Selector只能管理非阻塞的通道,FileChannel是阻塞的,无法管理。

AIO

异步非阻塞,用户进程只需要发起一个IO操作便立即返回,等 IO 操作真正完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据处理就好了,不需要进行实际的 IO 读写操作,因为真正的 IO 操作已经由操作系统内核完成了。