Skip to content

Java IO概述

1. Java IO的历史发展

IO的操作方式通常分为几种:同步阻塞BIO、同步非阻塞NIO、异步非阻塞AIO。

  1. 在 JDK1.4 之前,我们建立网络连接的时候采用的是BIO模式。
  2. Java NIO(New IO或Non Blocking IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO 支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。BIO与NIO一个比较重要的不同是,我们使用BIO的时候往往会引入多线程,每个连接对应一个单独的线程;而NIO则是使用单线程或者只使用少量的多线程,让连接共用一个线程。
  3. 在Java 7中引入了NIO的改进版AIO(NIO 2), 它是异步非阻塞的IO模型。

同步/异步与阻塞/非阻塞概念说明

  • 异步相⽐于同步最⼤的不同就是通过响应⽽不需要等待返回结果,可以继续⾃⼰的任务。
  • 非阻塞阻塞最⼤的不同就是不需要⼀直等待请求返回结果,可以继续执⾏⾃⼰的任务。

"同步∕异步"这组概念主要⽤于描述"请求-响应"的⽅式,它们定义到底是"同步"响应⽅式还是"异步"响应⽅式。⽽"阻塞∕⾮阻塞"这 组概念主要⽤于描述返回结果的⽅式,它们定义到底返回结果是"阻塞"的还是"⾮阻塞"的。

2. Java IO介绍

2.1 阻塞IO(BIO)介绍

阻塞IO(BIO)是最传统的一种IO模型,即在读写数据过程中会发生阻塞现象,直至有可供读取的数据或者数据能够写入。

  1. 在BIO模式中,服务器会为每个客户端请求建立一个线程,由该线程单独负责处理一个客户请求,这种模式虽然简单方便,但由于服务器为每个客户端的连接都采用一个线程去处理,使得资源占用非常大。因此,当连接数量达到上限时,如果再有用户请求连接,直接会导致资源瓶颈,严重的可能会直接导致服务器崩溃。
  2. 大多数情况下为了避免上述问题,都采用了线程池模型(也被称为伪异步IO模式解决办法)。就是创建一个固定大小的线程池,如果有客户端请求,就从线程池中取一个空闲线程来处理,当客户端处理完操作之后,就会释放对线程的占用。因此这样就避免为每一个客户端都要创建线程带来的资源浪费,使得线程可以重用。但线程池也有它的弊端,如果连接大多是长连接,可能会导致在一段时间内,线程池中的线程都被占用,那么当再有客户端请求连接时,由于没有空闲线程来处理,就会导致客户端连接失败。此外该模式在底层仍旧采⽤了同步阻塞的BIO模型,是⽆法从根本上解决问题的。
    传统的BIO模式如下图所示:
    Alt text

2.2 非阻塞IO(NIO)

基于BIO的各种弊端,在JDK1.4开始出现了高性能IO设计模式非阻塞IO(NIO)。

  1. NIO采用非阻塞模式,基于Reactor模式的工作方式,I/O调用不会被阻塞,它的实现过程是:会先对每个客户端注册感兴趣的事件,然后有一个线程专门去轮询每个客户端是否有事件发生,当有事件发生时,便顺序处理每个事件,当所有事件处理完之后,便再转去继续轮询。如下图所示:
    Alt text
  2. NIO中实现非阻塞I/O的核心对象就是SelectorSelector就是注册各种I/O事件地方,而且当我们感兴趣的事件发生时,Selector的select()方法就会返回这些事件给thread处理。

NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,一个选择器线程可以同时处理成千上万个连接,系统不必创建大量的线程,也不必维护这些线程,从而大大减小了系统的开销。适合的场景是连接数特别多,但都是短链接的场景。

IONIO
面向流面向缓冲区
阻塞IO非阻塞IO
选择器Selectors

2.3 异步非阻塞IO(AIO)

  1. AIO也就是NIO 2,在Java7中引入了NIO的改进版NIO 2,它是异步非阻塞的IO模型。异步IO是基于事件和回调机制实现的,也就是说AIO模式不需要selector操作,⽽是在服务器端的IO操作完成后,再给线程发出通知(通过异步回调事件机制)。因此,AIO模式是不会阻塞的,回调操作是在等待IO操作完成后由系统⾃动触发的。
  2. Java的AIO API其实就是Proactor模式的应用,和Reactor模式类似。Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的, Reactor中需要应用程序自己读取或者写入数据,而Proactor模式中,应用程序不需要进行实际的读写过程,它只需要从缓存区读取或者写入即可,操作系统会读取缓存区或者写入缓存区到真正的IO设备。

AIO相⽐于NIO具体有什么改进呢?

虽然NIO提供了⾮阻塞⽅法的实现,但本质上NIO的IO操作还是同步的(体现在轮询器Selector操作上)。具体来讲,就是NIO的服务器线程是在IO操作准备好时得到通知,接着就由这个线程⾃⾏进⾏IO操作,因此本质上是同步操作。