IO一直是软件开发中的核心内容之一,随着海量数据的增长以及分布式系统的发展,IO扩展能力越发重要,现有的IO机制经过不断完善,已经逐步证明了其高扩展性应用的能力,今天将逐步讲解Java提供了哪些IO方式,以及各IO方式之间的比较,下面一起看看吧:




同步/异步、阻塞/非阻塞、并行/并发

(1)同步:当发出一个功能调用时,在没有得到结果之前,该调用就不返回或停止执行后续操作。

(2)异步:当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作,当这个调用完成后,一般通过状态、通知和回调来通知调用者。

(3)阻塞:调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。

(4)非阻塞:在不能立刻得到结果之前,该调用不会阻塞当前线程,继续处理后续的操作。

(5)并发:当系统只有一个CPU时,操作系统只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行。

(6)并行:当系统有多个CPU时,可以存在当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行。

总结

阻塞/非阻塞是针对某一个事件(线程/进程)来说的。同步/异步是针对多个事件(线程/进程)来说的。阻塞调用和同步调用不同,对于同步调用来说,很多时候当前线程可能还是激活的,只是从逻辑上当前调用没有返回而已,这个线程可能也会处理其他的消息。

并行是指多个任务同时在跑,是真正地同时运行。并发通常是指多个任务交替使用CPU,同一时刻还是只有一个任务在跑。


BIO、NIO及AIO的分析与比较

Java提供的IO方式常常分为三类:同步阻塞IO,同步非阻塞IO,异步非阻塞IO

1 、同步阻塞IO(BIO)

原理:同步阻塞IO (blocking IO),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

具体应用:

  • Stream类流式主要用于操作二进制文件,
例如 InputStream/OutputStream,FileInputStream/FileOutputStream,GzipOutputStream...
  • Writer/Reader 主要用于操作文本类操作
例如 FileReader/FileWriter等
  • Buffer类, 带缓冲类的实现可以避免频繁的IO磁盘读写,
例如BufferdWriter/BufferdReader,BufferedOutputStream/BufferedInputStream注意事项(1)使用缓冲区类一定要加flush(2)使用BIO需要显式关闭, 常使用try-finally进行关闭2 、同步非阻塞IO(NIO)原理:服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。主要组成:
  • Channel(通道)

国内大多翻译成“通道”。Channel和IO中的Stream相似,属于同一级别。Stream是单向的,例如InputStream, OutputStream,而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。

实现:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel
  • Buffer(缓冲区)

一个Buffer对象是固定数量的数据的容器。其作用是一个存储器或分段运输区,在这

里数据可被存储并在之后用于检索。

实现:CharBuffer、 DoubleBuffer、MappedByteBuffer、HeapByteBuffer 等
  • Selector(选择区)
Selector会不断轮询注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作。属性:选择器(Selector)、可选择通道(SelectableChannel)、选择键(SelectionKey)
3 、异步非阻塞IO(AIO)AIO是对JDK1.4中提出的同步非阻塞I/O(NIO)的进一步增强,常常也被称为NIO.2原理:服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。主要实现:在不同的操作系统上, AIO由不同的技术实现
  • JAVA AIO框架在windows下使用windows IOCP技术
  • JAVA AIO框架在Linux下使用epoll多路复用IO技术模拟异步IO
主要组成(JDK1.7)

  • AsynchronousFileChannel: 用于文件异步读写;
  • AsynchronousSocketChannel: 客户端异步socket;
  • AsynchronousServerSocketChannel: 服务器异步socket。


I/O多路复用模式:Reactor和Proactor

I/O 多路复用机制(I/O multiplexing mechanisms)依赖于一个事件多路分用器(Event demultiplexor)。常见的事件多路分用器包括:Linux 的 EPOLL 和 Windows 的

IOCP,事件多路分用器相关的两个设计模式为 Proactor 和 Reactor。

Proactor用于异步IO,而Reactor用于同步IO

为了理解Reactor与Proactor二者的差异,以读操作为例进行比较:

在Reactor中实现读:


  • 注册读就绪事件和相应的事件处理器
  • 事件分离器等待事件
  • 事件到来,激活分离器,分离器调用事件对应的处理器。
  • 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。

在Proactor中实现读:


  • 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
  • 事件分离器等待操作完成事件
  • 在分离器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分离器读操作完成。
  • 事件分离器呼唤处理器。
  • 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分离器。

总结:Reactor和Proactor模式的主要区别就是真正的读取和写入操作是有谁来完成的,Reactor中需要应用程序自己读取或者写入数据,而Proactor模式中,应用程序不需要进行实际的读写过程,它只需要从缓存区读取或者写入即可,操作系统会读取缓存区或者写入缓存区到真正的IO设备。


BIO、NIO及AIO的应用场景

(1)BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,

并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

(2)NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,

并发局限于应用中,编程比较复杂,JDK1.4开始支持。

(3)AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,

充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。



BIO、NIO及AIO的解析与比较_数据