看了你就懂的同步与异步、阻塞与非阻塞

前言

在网上看到过很多讲有关同步与异步阻塞与非阻塞的文章,但是很多都是抛出一堆相关定义,看了之后还是云里雾里的,对这几个概念还是不能很好的去区分它们。本文通过通俗易懂的语言和相关例子让你深入理解其本质。

同步与异步

首先我们要明确的是,同步和异步都是针对两个或者两个以上的事物来说的。比如当我们在网上购物看中一件物品,然后去浏览该商品详情的时候,首先页面会先发送一个请求,后台服务器查询对应商品的相关数据,然后前端详情页面才根据返回数据展示该商品的详细信息。而此时你的网速比较差,一个详情页面等了将近一分钟才全部展示完成,这时候你问这个请求是同步还是异步?答案显然是同步请求,它给我们最直观的表现形式就是页面一直显示在加载中,商品的详情页面渲染必须要等待后台服务器返回商品详情数据后才能进行。也就是说下一个操作必须要等待上一个操作完成才能进行,它依赖于上一个操作的返回结果。

你可能会问,在同步的情况下,当一个事物正在进行操作的时候,其它的事物此时在干嘛呢?这个实际上并没有明确的规定,其实同步更多的是关注事物一个一个的串行执行的过程,保证不会交叉执行,至于某个时刻处于什么状态并不关心。这在计算机中大部分时候其它事物都是处于一个等待的状态,而我们人则要灵活得多,在我们日常生活中常用的同步手段就是排队,比如我们上下班坐地铁进行安检的时候,需要依次排队安检进站乘车,但是你在排队的过程是在看手机、聊天还是什么也不做都可以,安检人员并不会在意你在做什么,这种就是由于安检资源有限导致的同步。

sync-async.jpg

对于同步这里有两个点需要注意,一是同步的范围,有时候并不需要全局的大范围的去同步,只需要在特定的操作同步即可,这样可以提升执行效率,比如 Java 语言中的同步方法和同步代码块。另一个是同步的粒度,并不是在一些大的操作粒度上才需要同步操作,小的粒度操作也需要同步操作,只是有的小粒度操作天然就已经是同步操作,并不需要我们人为的去添加同步操作控制。比如 Java 语言中的同步都是针对有两个或者两个以上线程的程序来说的,因为单线程的程序里它天然就是同步的。
异步则完全相反,在异步情况下多个事务可以同时进行,互不影响,你进行你的,我进行我的,谁都不用关心谁。总的来说就是:

  • 同步 两个事物相互依赖,并且一个事物必须以依赖于另一事物的执行结果。比如在事物 A->B 事件模型中,你需要先完成事物 A 才能执行事物 B。也就是说,同步调用在被调用者未处理完请求之前,调用不返回,调用者会一直等待结果的返回。
  • 异步 两个事物完全独立,一个事物的执行不需要等待另外一个事物的执行。也就是说,异步调用可以返回结果不需要等待结果返回,当结果返回的时候通过回调函数或者其他方式带着调用结果再做相关事情。

可以看出同步与异步是从行为角度描述事物的,你品,你细品。(PS:这里的多个事务可以指代不同的操作、不同的方法或者不同的代码语句等等

阻塞与非阻塞

所谓阻塞,简单来说就是发出一个请求不能立刻返回响应,要等所有的逻辑全处理完才能返回响应。非阻塞反之,发出一个请求立刻返回应答,不用等处理完所有逻辑。阻塞与非阻塞指的是单个线程内遇到同步等待时,是否在原地不做任何操作。
堵车就是阻塞与非阻塞最好的例子,在一线城市生活过的朋友应该都有体会,在交通正常的时候汽车可以正常通行,就是非阻塞,上下班高峰的时候经常发生堵车,交通正常的时候半个小时车程,高峰期可能需要二、三个小时才能到。。。而且一旦发生交通堵塞,所有马路上的车子都一动不动,只能在车子里等待,就是阻塞,当然大多数人不会选择干等,他们会玩手机或者和朋友聊天等等,同样的在计算机里,阻塞就意味着停止执行停下来等待,非阻塞表明操作可以继续向下执行,但是在发生阻塞的时候计算机可就没有像人这么灵活了,通常计算机的处理方式就是挂起当前线程,然后干等着,阻塞结束后才继续执行该线程。可以看出阻塞和非阻塞描述的当前事物的状态(等待调用结果时的状态)。

recv-wait.jpeg

结合前面介绍的同步与异步,两两组合就会有四种情况,分别是同步阻塞同步非阻塞异步阻塞异步非阻塞。下面通过车道的例子来形象的解释这几种状态:

  • 同步阻塞 只有一个车道,不能超车,所有车子依次行使,一次只能通过一辆车,尴尬的是这个车道还堵车了。
  • 同步非阻塞 只有一个车道,不能超车,所有车子依次行使,一次只能通过一辆车,不过比较幸运这个车道没有堵车,可以正常通行。
  • 异步阻塞 有两个或两个以上车道,每条马路都可以通行,不同车道上的车子可以并行行使,尴尬的是所有的车道都堵车了。
  • 异步非阻塞 有两个或两个以上车道,每条马路都可以通行,不同车道上的车子可以并行行使,不过比较幸运的是没有一个车道堵车,都可以正常通行。

对应到我们计算机里也是一样的,同步阻塞相当于只有一个线程,而且该线程处于阻塞(Blocked)状态,同步非阻塞相当于只有一个线程,而且该线程处于运行(Running)状态。异步阻塞相当于有多个线程,而且所有线程都处于阻塞(Blocked)状态,异步非阻塞相当于有多个线程,而且所有线程都在正常运行。

总结

很多程序思想都来源于生活,需要我们自己去寻找身边的场景多类比思考、总结归纳,这样才会理解得更深刻。

阅读 314

推荐阅读