基本概念
同步与异步: 同步与异步是针对应用程序跟内核的交互而言的,具体指消息通知机制,对如何处理消息并不关心.
阻塞和非阻塞: 阻塞和非阻塞是指程序在等待消息/返回值时的状态,阻塞调用在调用结果返回之前当前线程被挂起,非阻塞调用在不能立即得到结果之前该调用不会阻塞当前线程. 是针对于进程在访问数据的时候,根据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开始支持.