龙空技术网

同步和异步,阻塞和非阻塞,用一个生活场景简单说明

人月聊IT 343

前言:

今天我们对“java中阻塞和非阻塞”可能比较重视,同学们都想要知道一些“java中阻塞和非阻塞”的相关知识。那么小编也在网摘上搜集了一些有关“java中阻塞和非阻塞””的相关文章,希望姐妹们能喜欢,看官们一起来学习一下吧!

对于阻塞和非阻塞,同步和异步,再加上两者的组合实际上不太容易理解的概念,今天重新思考了下再做下整理和总结,供大家参考。

概述

对于传统的IO是面向流的,也常称为IO流,在这种情况下一个请求就对应一个线程,如果存在大并发访问的,且请求时间长的情况,就导致大量线程阻塞和挂起,快速消耗完线程池的所有线程,因此IO阻塞模式下很难应对高并发访问。

在这个基础上出现了NIO非阻塞模式,先来看下NIO的一个定义说明。

java.nio全称java non-blocking IO(实际上是 new io),是指JDK 1.4 以上版本里提供的新api(New IO) ,为所有的原始类型(boolean类型除外)提供缓存支持的数据容器,使用它可以提供非阻塞式的高伸缩性网络。

NIO模式下最大的好处就是请求线程发送请求给对方,对方在长时间的执行过程中,请求线程可以释放回线程池,或者重新去做其它事情,这样就极大的减少的被挂起和无效的空等待时间消耗。

除了阻塞和非阻塞,还有同步和异步两个概念。

同步和异步

对于同步和异步我在前面谈SOA和接口服务,消息中间件的时候经常会谈到。简单来讲同步就是连接一直保持着,等待对方返回结果。而异步则是先发出请求后直接返回,连接也关闭,同时通过一个轮询或一个回写接口来获取返回信息。

阻塞和非阻塞

而主阻塞和非阻塞实际是针对请求者来说的,则在发送请求后是否需要一直处于等待状态,如果是的话则是阻塞,否的话则是非阻塞。

当重新思考这两个概念的时候,还是准备将一个请求拆分为请求线程+执行线程两个阶段来进行理解。即同步异步更多指的是执行线程的执行过程是异步的,是通过排队等待的。而对于阻塞和非阻塞针对的是请求线程,即请求发送后不用保存连接等待。

比如张三发出请求给李四,让李四帮忙去泡茶。这个时候李四马上行动对泡茶就是同步,但是李四可能在忙其它事情,只是回复了张三我你的请求我已经收到了,那么张三请求是进入了队列等待执行,这个时候就是异步。

而对于阻塞和非阻塞来说,张三发起请求给李四,李四马上去执行泡茶,然后张三不能做其它事情一直在那等待。这个时候即是阻塞。但是张三发起请求给李四,张三就去忙其它事情,每5分钟都来问下李四茶泡好了没有。这个是非阻塞。重点是张三发出请求后能够去干其它事情。

两者组合场景说明

对于两者的组合实际上存在四种类型,即同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。

同步阻塞:请求方等待,执行方立刻执行。同步非阻塞:请求方不等待,执行方立刻执行。异步阻塞:请求方等待,执行方排队。异步非阻塞:请求方不等待,执行方排队。

比如常说的同步非阻塞则是张三发出请求给李四,李四马上执行泡茶,张三去溜达或干其它事,然后轮询茶泡好没有,直到拿到自己的茶。

而异步下谈阻塞或非阻塞没有意义。异步下本身请求线程马上就得到了返回,请求过程就已经结束,不存在请求线程还要挂起的情况。异步下不存在 非阻塞的必要性。同步下请求和执行线程是绑定的,异步下请求和执行线程是解耦开的,真正的执行线程是完全新启的独立线程。

餐厅吃饭的场景

我们再拿去餐厅吃饭的场景来举例说明,在这个场景里面有具体的顾客,有服务员,有后厨的厨师。在这里服务员是一组我们理解为请求线程,而后台厨师为一组,我们理解为执行线程。而顾客仅仅是请求发起方。

我们以张三去餐厅吃饭,叫来服务员点了一份牛排进一步举例说明。

同步阻塞模式

张三点了牛排,服务员将点菜单交给厨房里面的厨师,这个时候厨师马上就去煎制这份牛排,而服务员也不去服务其它顾客,一直在那等着牛排做好后端给顾客为止。

同步非阻塞模式

张三点了牛排叫给服务员,服务员通知到厨师,厨师也马上开始做牛排。但是差别在于服务员不用再那等着牛排做好。而是去服务李四这个顾客,服务完后又到后厨窗口去问下牛排煎好没有?如果还没有煎好,服务员又去服务王五完成后又去轮询牛排煎好没。当厨师说已经煎好了,那么服务员将牛排端给顾客服务完成。

在这种场景下请求线程和执行线程分离,请求线程在送达请求后无须等待而是回到请求线程池,可以释放出来后做其它事情。

异步阻塞模式

服务员将点餐单拿到后厨,但是后台厨师都忙,服务员通知后厨张三新点了份牛排。后台厨师说知道了,你先把单子放在那,前面客人点的菜做完了就做这个牛排。

如果服务员将单子通知到后厨,后厨没有答复请求已经收到,那么这个时候服务员不等走,还是处于等待状态,一直等到后厨答复已经收到请求了才能够离开。

异步非阻塞模式

服务员将点餐单拿到后厨窗口就走掉了,这个时候可能后厨并没有答复新的点餐请求是否收到。服务员在服务完另外一个顾客后,回来再询问请求是否收到,后厨答复请求已经收到了,这个时候请求过程结束。

注意异步模式下一般不谈阻塞或非阻塞,具体原因是服务员将点餐单拿到后厨的时候,实际厨房反馈请求收到,这个过程相当短就可以结束,完全没有必要还没有等到厨房反馈请求收到就跑开了。

轮询或消息通知

在谈同步异步的时候,经常会说到异步调用场景下需要通过轮询或提供异步回写消息接口来获取最终的结果。但是在非阻塞情况下可以看到,非阻塞也需要通过轮询来获取结果。

还是前面的例子说明。

服务员将点餐单给到后台厨房后,厨师说收到请求,已经在进入排队。这个时候有两种方式来获取最终的结果返回。

其一就是服务员定期去询问下后台厨房,张三点的牛排是否已经做好了。其二是厨师做好了牛排后,敲下铃铛或叫一声,张三的牛排就可以取了。

对于第一种方式即常说的轮询模式,而第二种方式则是消息回写。在消息回写模式下当你得到通知你的菜已经做好后,你还需要新发起一个线程将东西取回来。

NIO同步非阻塞适用场景

同步非阻塞往往是一个常用的场景。

简单来说对于请求端来说整体的感受仍然是同步请求,但是内部请求线程却没有被阻塞一直处于等待状态,这样就完全加大了中间件进行大并发访问和调用的处理性能和能力。这也是当前很多中间件采用NIO线程的一个关键原因。

对于接口服务调用哪种场景适合NIO?

对于传统的ESB服务总线或API网关在注册和接入接口服务的时候,可以看到存在一种接口服务调用,该类接口提供方在收到请求后需要经过后台长耗时的计算或查询处理才能够返回数据。在这种情况在实际请求端的连接处于一直保持状态。

如果存在大并发的类似接口服务调用,往往很快消耗完整个线程池可用线程。因此在这种情况下实际上适合采用同步NIO非阻塞模式来进行处理。

标签: #java中阻塞和非阻塞 #java中阻塞和非阻塞什么意思 #java同步异步区别 #同步请求与异步请求 #异步请求和同步请求的区别