在网上看到过很多讲有关 同步与异步
、 阻塞与非阻塞
的文章,但是很多都是抛出一堆相关定义,看了之后还是云里雾里的,对这几个概念还是不能很好的去区分它们。本文通过通俗易懂的语言和相关例子让你深入理解其本质。
首先我们要明确的是, 同步和异步都是针对两个或者两个以上的事物来说的
。比如当我们在网上购物看中一件物品,然后去浏览该商品详情的时候,首先页面会先发送一个请求,后台服务器查询对应商品的相关数据,然后前端详情页面才根据返回数据展示该商品的详细信息。而此时你的网速比较差,一个详情页面等了将近一分钟才全部展示完成,这时候你问这个请求是同步还是异步?答案显然是同步请求,它给我们最直观的表现形式就是页面一直显示在加载中,商品的详情页面渲染必须要等待后台服务器返回商品详情数据后才能进行。也就是说 下一个操作必须要等待上一个操作完成才能进行
,它依赖于上一个操作的返回结果。
你可能会问,在同步的情况下,当一个事物正在进行操作的时候,其它的事物此时在干嘛呢?这个实际上并没有明确的规定,其实同步更多的是关注事物一个一个的串行执行的过程,保证不会交叉执行,至于某个时刻处于什么状态并不关心。这在计算机中大部分时候其它事物都是处于一个 等待
的状态,而我们人则要灵活得多,在我们日常生活中常用的同步手段就是 排队
,比如我们上下班坐地铁进行安检的时候,需要依次排队安检进站乘车,但是你在排队的过程是在看手机、聊天还是什么也不做都可以,安检人员并不会在意你在做什么,这种就是由于安检 资源有限
导致的同步。
对于同步这里有两个点需要注意,一是 同步的范围
,有时候并不需要全局的大范围的去同步,只需要在特定的操作同步即可,这样可以提升执行效率,比如 Java
语言中的同步方法和同步代码块。另一个是 同步的粒度
,并不是在一些大的操作粒度上才需要同步操作,小的粒度操作也需要同步操作,只是有的小粒度操作天然就已经是同步操作,并不需要我们人为的去添加同步操作控制。比如 Java
语言中的同步都是针对有两个或者两个以上线程的程序来说的,因为单线程的程序里它天然就是同步的。
而 异步
则完全相反,在异步情况下多个事务可以同时进行,互不影响,你进行你的,我进行我的,谁都不用关心谁。总的来说就是:
A->B
事件模型中,你需要先完成事物 A 才能执行事物 B。也就是说,同步调用在被调用者未处理完请求之前,调用不返回,调用者会一直等待结果的返回。
可以看出同步与异步是从 行为角度
描述事物的,你品,你细品。(PS: 这里的多个事务可以指代不同的操作、不同的方法或者不同的代码语句等等
)
所谓 阻塞
,简单来说就是发出一个请求不能立刻返回响应,要等所有的逻辑全处理完才能返回响应。 非阻塞
反之,发出一个请求立刻返回应答,不用等处理完所有逻辑。阻塞与非阻塞指的是 单个线程内
遇到同步等待时,是否在原地不做任何操作。
堵车
就是阻塞与非阻塞最好的例子,在一线城市生活过的朋友应该都有体会,在交通正常的时候汽车可以正常通行,就是 非阻塞
,上下班高峰的时候经常发生堵车,交通正常的时候半个小时车程,高峰期可能需要二、三个小时才能到。。。而且一旦发生交通堵塞,所有马路上的车子都一动不动,只能在车子里等待,就是 阻塞
,当然大多数人不会选择干等,他们会玩手机或者和朋友聊天等等,同样的在计算机里,阻塞就意味着停止执行停下来等待,非阻塞表明操作可以继续向下执行,但是在发生阻塞的时候计算机可就没有像人这么灵活了,通常计算机的处理方式就是挂起当前线程,然后干等着,阻塞结束后才继续执行该线程。可以看出阻塞和非阻塞描述的 当前事物的状态
(等待调用结果时的状态)。
结合前面介绍的 同步与异步
,两两组合就会有四种情况,分别是 同步阻塞
、 同步非阻塞
、 异步阻塞
和 异步非阻塞
。下面通过车道的例子来形象的解释这几种状态:
对应到我们计算机里也是一样的,同步阻塞相当于只有一个线程,而且该线程处于阻塞(Blocked)状态,同步非阻塞相当于只有一个线程,而且该线程处于运行(Running)状态。异步阻塞相当于有多个线程,而且所有线程都处于阻塞(Blocked)状态,异步非阻塞相当于有多个线程,而且所有线程都在正常运行。
很多程序思想都来源于生活,需要我们自己去寻找身边的场景多类比思考、总结归纳,这样才会理解得更深刻。