Simple BIO, NIO, AIO in Java

基本概念

同步与异步: 同步与异步是针对应用程序跟内核的交互而言的,具体指消息通知机制,对如何处理消息并不关心.

阻塞和非阻塞: 阻塞和非阻塞是指程序在等待消息/返回值时的状态,阻塞调用在调用结果返回之前当前线程被挂起,非阻塞调用在不能立即得到结果之前该调用不会阻塞当前线程. 是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了就是一种读取或写入操作函数的实现方式. 阻塞方式下读取/写入函数一直等待操作完成,而非阻塞方式下,该操作函数会立即返回一个状态值.

由上面的基本描述可以进行总结: 同步/异步是目的,阻塞/非阻塞是实现方式.

详细解释

同步

指的是用户进程触发IO操作并等待或轮询的方式去去查看IO操作是否就绪,即发出一个功能调用时,在没有得到结果之前该调用就不返回.

按照这个定义,大部分的函数/方法调用都是同步的,比如一些内建的math函数,但是一般而言,我们在说同步/异步的时候,特指那些需要其他部件或需要一定时间才能完成的任务.

异步

指用户进程触发IO操作后便开始做别的事情,而当该IO操作完成的时候该进程会得到完成的通知(异步的特点就是通知, 使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS).

即当一个异步过程调用发出后,调用者不会立即得到结果,实际处理这个调用的部件会在该调用发出后,通过状态/通知的方式来通知调用者,或通过回调函数处理这个调用.

阻塞

所谓阻塞的方式是指,当试图对文件描述符进行读写时,如果当时没有数据可读,或者暂时不可写,程序就进入等待状态,直到有数据读或可写为止.

即调用结果返回之前,当前线程会被挂起,不能做其他任何操作,函数只有在得到结果之后才会返回.

非阻塞

非阻塞状态下,如果没有东西可读,或者不可写,读写函数马上返回,不会等待,而去进行其他操作. 即不会阻塞当前线程,会立即返回.

IO组合

同步阻塞IO(BIO): 同步并阻塞, 效率最低,即专心排队,什么别的事都不做.

异步阻塞IO(NIO): 使用异步的消息触发方式,比如在银行领一个序号,但是领取序号后不能做别的事情,此时这个人又被阻塞在等待上了.

同步非阻塞IO(NIO): 同样效率低下,比如一边打电话一边去看队伍轮到你了没有,把打电话和观察队伍看成是程序的两个操作的话,这个程序需要在两种行为间不停的切换,造成效率低下. 服务器实现为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理.用户进程也需要时不时的询问IO是否就绪,这就要求用户进程不停的询问.

异步非阻塞IO(AIO,NIO.2): 比如领完序号后就可以随意做别的事情,而”叫号”通知你则是柜台的事情,程序不需要在两种行为间切换. 用户进程只需要发起一个IO操作后立即返回,IO操作真正完成之后应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要实际的IO读写操作,因为真正的IO读取或写入操作已经由内核完成了.

实例解释

这里摘抄知乎上相关问题的回答,比喻比较形象.

出场人物:老张,道具:普通水壶,会响水壶

1: 同步阻塞: 老张把水壶放火上,立等水开.

2: 同步非阻塞: 老张把水壶放火上,去客厅看电视,时不时去厨房看水有没有开.

3: 异步阻塞: 来张把水壶放到火上,立等水开.

4: 异步非阻塞: 老张把会响的水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶.

所谓同步/异步是针对水壶而言: 普通水壶的水开没开,只能让调用者去轮询,造成老张效率低下. 会响水壶在水开之后会提示老张.

所谓阻塞与非阻塞是针对老张而言: 立等水开的老张为阻塞,看电视的老张为非阻塞.这里跟判断水开的方式无关,即自己去看(同步)还是水壶自己响(异步).

情况1和3时,老张就是阻塞的,老张这个当前线程被挂起,为了等获取水开得结果不能再去做其他事情, 虽然3中的响水壶以异步方式(水壶响)通知水开,当老张是立等(阻塞)的方式接收到结果.

情况2时,虽然可以去做其他事情(不阻塞),但是老张需要不停的去查看水开了没有,这样需要在客厅和厨房来回跑,造成效率低下.

情况4时,异步与无阻塞相结合,发挥最大效率.

适用场景

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用.

NIO方式使用于连接数目多且短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程模型发杂,JDK1.4开始支持.

AIO方式适用于连接多且长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程模型不复杂,JDK7开始支持.

参考连接

参考连接1
参考连接2
参考连接3