如何理解:同步、异步、阻塞、非阻塞、BIO、NIO、AIO?

有关于同步/异步/阻塞/非阻塞/BIO/NIO/AIO的内容是面试的高频问题了,网上相关内容很多,汇总起来的很少。

常见的误区

假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用合适的视图站视详情数据。

如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTTP响应,然后继续往下执行。

这个时候你问别人,刚刚代码发起的这个请求是不是一个同步请求,对方一定回答是,这是对的。

但是我们要问为什么是呢?很多人都会说:“因为发起请求之后,代码就卡住不动了,直到拿到响应之后才可以继续往下执行。”【这是不对的,因果关系弄反了!】

因为内核空间里的对应函数会卡住不动,造成用户空间发起的系统调用卡住不动,继而使程序里的用户代码卡住不动了。卡着不动知识同步请求的一个副作用,并步能用它来定义同步请求,那么该如何定义呢?

同步:就是实时处理(打电话)

可以理解为在执行完一个函数或者方法之后,一直等待系统返回值或者消息,这时程序是出于阻塞的,只有接受到返回值或者消息后才能往下执行其他的命令。

也就是服务器一接受到客户请求,马上相应,这样客户端可以在最短时间内得到结果,但是如果多个客户端,或者 一个客户端发出请求很频繁的时候,服务器午发同步处理,就会造成拥塞。

同步像是打电话,通信双方不断(我们是同时进行)你一句我一句这样的好处是:对方想表达的信息我马上就能收到,但是我在打电话,我无法做别的事情。

异步:就是分时处理(如收发短信)

服务器接受到客户端请求后并不是立即处理,而是等待服务器比较空闲的时候加以处理,可以避免涌塞。

异步如同收发短信,打电话是双方都要在线,短信呢,另一方是不用在手机旁的,也不用一直等着手机来没有短信。

对于写程序,同步往往会阻塞,没有数据过来,就一直等着,异步就步会阻塞。没有数据我就干别的事情,有数据的时候我再处理数据。

下面我们再来看看图解:

阻塞非阻塞

阻塞与非阻塞是描述线程再访问某一个资源的时候,数据是否准备就绪的一种处理方式,当数据没有准备就绪的时候:

  • 阻塞:线程持续等待资源中数据准备完成,直到返回响应结果。
  • 非阻塞:线程池直接返回结果,不会持续等待资源准备数据结束后才响应结果。

我们可以把阻塞理解成堵车,一动不动;非阻塞就是路通了。

计算机当中,一般阻塞的时候只能等着,因为这是最容易实现的,只需要挂起线程,让出CPU即可,再满足条件的时候会重新调度该线程。

  • 所谓同步/异步,关注的是能不能同时开工。
  • 所谓阻塞/非阻塞,关注的是能不能动。

通过推理进行组合:

同步阻塞,不能同时开工,也不能动。只有一条小道,一次只能过一辆车,可悲的是还TMD的堵上了。

同步非阻塞,不能同时开工,但可以动。只有一条小道,一次只能过一辆车,幸运的是可以正常通行。

异步阻塞,可以同时开工,但不可以动。有多条路,每条路都可以跑车,可气的是全都TMD的堵上了。

异步非阻塞,可以工时开工,也可以动。有多条路,每条路都可以跑车,很爽的是全都可以正常通行。

是不是很容易理解啊。其实它们的关注点是不同的,只要搞明白了这点,组合起来也不是事儿。

回到程序里,把它们和线程关联起来:

  • 同步阻塞,相当于一个线程在等待。
  • 同步非阻塞,相当于一个线程在正常运行。
  • 异步阻塞,相当于多个线程都在等待。
  • 异步非阻塞,相当于多个线程都在正常运行。

I/O

指的就是读入/写出数据的过程,和等待读入/写出数据的过程。一旦拿到数据后就编程数据操作了,步是IO了。

拿网络IO来说等待的过程就是数据从网络到网卡再到内核的空间,读写的过程就是内核空间和用户空间的互相拷贝。

所谓的IO就包括两个过程,一个是等待数据的过程,一个是读写(拷贝)数据的过程。而且还要明白,一定不能包括操作数据的过程。

阻塞IO和非阻塞IO

应用程序都是运行再用户空间的,所以它们能操作的数据也都在用户空间。按照这样来理解,只要数据没有达到用户空间,用户线程就操作不了。

如果此时用户线程已经参与,那它一定会呗阻塞在IO上,这就是常说的阻塞IO。用户线程呗阻塞在等待数据上或者拷贝数据上。整个流程都是采用一个线性的处理方式:请求数据—>等待数据准备就绪—>等待数据拷贝—>数据拷贝完成,返回结果,但是!如果数据准备过程非常长的话,整个进程的阻塞时间也会非常长,严重影响应用处理效率。

处理流程:

如果此时用户线程已经参与,那它一定会被阻塞在IO上。这就是常说的阻塞IO。用户线程被阻塞在等待数据上或拷贝数据上。

非阻塞IO就是用户线程不参与以上两个过程,即数据已经拷贝到用户空间后,才去通知用户线程,一上来就可以直接操作数据了。

用户线程没有因为IO的事情出现阻塞,这就是常说的非阻塞IO。

处理流程:

同步IO和同步阻塞IO

按照上文中对同步的理解,同步IO是指发起IO请求后,必须拿到IO的数据才可以继续执行。

按照程序的表现形式又分为两种:

  • 在等待数据的过程中,和拷贝数据的过程中,线程都在阻塞,这就是同步阻塞IO。
  • 在等待数据的过程中,线程采用死循环式轮询,在拷贝数据的过程中,线程在阻塞,这其实还是同步阻塞IO。

网上很多文章把第二种归为同步非阻塞IO,这肯定是错误的,它一定是阻塞IO,因为拷贝数据的过程,线程是阻塞的。

严格来讲,在IO的概念上,同步和非阻塞是不可能搭配的,因为它们是一对相悖的概念。

同步IO意味着必须拿到IO的数据,才可以继续执行。因为后续操作依赖IO数据,所以它必须是阻塞的。

非阻塞IO意味着发起IO请求后,可以继续往下执行。说明后续执行不依赖于IO数据,所以它肯定不是同步的。

因此,在IO上,同步和非阻塞是互斥的,所以不存在同步非阻塞IO。但同步非阻塞是存在的,那不叫IO,叫操作数据了。

所以,同步IO一定是阻塞IO,同步IO也就是同步阻塞IO。

异步IO和异步阻塞/非阻塞IO

按照上文中对异步的理解,异步IO是指发起IO请求后,不用拿到IO的数据就可以继续执行。

用户线程的继续执行,和操作系统准备IO数据的过程是同时进行的,因此才叫做异步IO。

按照IO数据的两个过程,又可以分为两种:

  • 在等待数据的过程中,用户线程继续执行,在拷贝数据的过程中,线程在阻塞,这就是异步阻塞IO。
  • 在等待数据的过程中,和拷贝数据的过程中,用户线程都在继续执行,这就是异步非阻塞IO。

第一种情况是,用户线程没有参与数据等待的过程,所以它是异步的。但用户线程参与了数据拷贝的过程,所以它又是阻塞的。合起来就是异步阻塞IO。

第二种情况是,用户线程既没有参与等待过程也没有参与拷贝过程,所以它是异步的。当它接到通知时,数据已经准备好了,它没有因为IO数据而阻塞过,所以它又是非阻塞的。合起来就是异步非阻塞IO。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值