一篇文章理解阻塞、非阻塞、同步、异步
目录
- 理解阻塞、非阻塞、同步、异步
- 阻塞
- 非阻塞
- 同步
- 异步
- 总结
理解阻塞、非阻塞、同步、异步
首先说明,这些都是在特点场景下或者相对情况的词汇,OK,接下来开门见山。
阻塞
可以很直观的理解,就如节假日高速路出口收费站一样,上图片:
9个收费亭,同时来了一大波车,这时候同一时刻只能有9辆车在收费,剩下的车都在只能在后面排队等待,这就是现实中很直观的阻塞现象。这9个收费亭,就是一个瓶颈,或许画为这样更符合大家对瓶颈二字的理解:
第1张图中,高速公路源源不断的车辆到来,和第二张图的效果其实表示一样。
OK,看图明白了现象,分析一下为什么会阻塞?
1.数量上:
到来车辆数——大量
收费站数——小于等于9个
结论:在要过卡的汽车数量大于收费亭数量时,就会有阻塞现象。
2.速度上:
到来车辆速度——快速
收费站过卡速度——慢
结论:在收费站过卡速度比车辆到来的速度慢时,就会有阻塞现象。
综合起来就是:因为量差和速度差,导致阻塞现象。
思考问题,为什么会有量差?
因为有些资源是有限的,是很难避免的,高速公路出口区域的大小有限,收费亭的个数会根据合理的规划设立,即使设立了1千个收费亭,从高速路到来的汽车跑到距离最远的那个收费亭也是相当远,没有车主愿意跑那么远去收费,它就形同虚设,有效收费亭数就还是一个相对很小的数量。同时,还需要考虑成本因素。
在程序里,比如数据库连接池里的连接是有限的,比如10条连接,但1毫秒内需要做1000个查询,就会形成阻塞现象。
而速度差是客观存在的,收费亭还需要经过不断的发展,才能达到和高速公路相匹配的速度,但收费亭还有一个作用就是让高速的车辆减速下来,去匹配非高速公路的速度。
在程序里,数据库查询,需要经过网络IO和磁盘IO,相同的内容怎么都比在本机内存中直接检索出来要慢。
阻塞,其实是一个客观存在的现象,它本质上是没法绕开的。
既然绕不开,那……非阻塞又是什么?
非阻塞
还是上面的例子,车辆经过高速路收费亭,非阻塞更像是改版的ETC,车辆进高速,扫一下车牌登记一下,车辆离开高速,扫一下车牌登记一下,然后车辆离开了,开出个几百米后车主手机才收到ETC被扣费的短信,此时高速路收费才算完成。整个过程,停留的时间很短,如果车牌识别效率非常高,甚至可以把车卡的杆去掉,这样车辆就无需停留。
无需停留即速度与车辆到来速度相匹配,即没有阻塞现象。
那是真的没有阻塞了吗?怎么可能,只是从车的角度来看,车确实不阻塞了,但从整个收费程序来看,车辆跑出几百米后才收费成功,就表示实际上自动扣费的速度比较慢,阻塞范围缩小到了自动扣费上。
把阻塞范围缩小,缩短主体停留时间,就是非阻塞要做的事情。
到这里,先记住这个结论,先折起一小部分内容留最后总结联系上下文……
同步
下班回家到家门口的时候,开门经过以下步骤:
- 1.掏钥匙(还需要从几百把钥匙里挑选钥匙请忽略钥匙的步骤)
- 2.插入门锁孔(磁卡锁、指纹锁、人脸锁等,请积极回忆用钥匙的日子)
- 3.旋转钥匙,开门
正常来说,三个步骤是顺序依赖的,这三步骤你怎么换人分着做,都会等待前一个步骤完成。
这时候,如果没有别的事情干扰,基本上我们会一个人去完成整个开门的事情,因为换人,也需要时间。
开门的人,看作一个主体;整个开门过程,可以看作一个事务。那么:
一个主体独自完成一个事务,便可以认为这个过程是同步的。
在程序里,给员工张三发一个节日祝福短信,步骤相似:
public static void main(String[] args) { // 给员工张三发一个节日祝福短信,步骤相似: // 1. 先把员工张三的信息查找出来 Employee employee = findEmployee("张三"); // 2. 编辑短信:”祝张三先生节日快乐,阖家幸福!“ String message = "祝张三" + employee.getGender() + "节日快乐,阖家幸福!"; // 3. 调用短信发送API发送短信内容到员工的手机号码 sendMessage(employee.getPhone(), message); }
1.先把员工张三的信息查找出来
2.编辑短信:”祝张三先生节日快乐,阖家幸福!“
3.调用短信发送API发送短信内容到员工的手机号码
整个事务都在一条线程里顺序完成,则属于同步操作。
同步的核心,是一个主体。主要看你把什么定为一个主体。
异步
接着上面,同步是一个主体做事,那么异步,就是多个主体做事。
比如开门的例子,如果把主体具体到手,右手在做开门这些步骤时,左手可能在摘下口罩,这时候两件事情都不冲突,摘下口罩后,还可以挠挠头,抓抓痒,左手可以为所欲为(左手千万别掰断右手)。
同一时刻,多个主体在做事,就属于异步。
在程序里,线程1给张三发节日祝福短信,线程2给李四发节日祝福短信,线程3给王五发,完全没有问题,为所欲为有木有。
当然,如果多个线程在做相同的事情,也可以叫并发。
思考问题,什么时候建议异步?
当多个事情没有冲突,而你又有足够的资源去同时展开工作时。
比如边开门边挠头的例子,如果你的左手因为数钱导致短暂性发麻无力,只有右手可以活动,那么边开门边挠头只会让你在切换这两件事的时候花费更多的时间。
在代码里,如果想要给张三同时发出去短信和邮件,则可以使用异步的方式去实现:
public static void main(String[] args) { // 给员工张三发一个节日祝福短信,步骤相似: // 1. 先把员工张三的信息查找出来 Employee employee = findEmployee("张三"); // 开启线程2去发邮件 new Thread(() -> {// 这里边的就是异步操作 // 编辑邮件 String mailMessage = "祝<h3>张三</h3>" + employee.getGender() + "节日快乐,阖家幸福!"; // 发送邮件 sendEmail(employee.getEmail(), mailMessage); }).start(); // 2. 编辑短信:”祝张三先生节日快乐,阖家幸福!“ String message = "祝张三" + employee.getGender() + "节日快乐,阖家幸福!"; // 3. 调用短信发送API发送短信内容到员工的手机号码 sendMessage(employee.getPhone(), message); }
1.先把员工张三的信息查找出来
2.线程1(main线程):编辑短信;线程2:编辑邮件
3.线程1(main线程):发送短信;线程2:发送邮件
线程2在start()后,main线程就可以继续往下执行了,main线程并不会等待线程2执行完成,也就是说,异步有一个特点——非阻塞。
异步可以加上回调这个利器,在执行出结果时,通过回调的方式,去反馈结果,这里不展开细谈。
总结
因为部分资源有限,所以阻塞客观存在的,可以简单的理解为有排队等待的现象,就是阻塞。
非阻塞主要是把阻塞范围缩小,或者把可以延迟完成的事情异步完成,缩短主体停留时间。
最后回到收费亭的非阻塞例子,车辆在经过出高速的收费亭登记后,就让另一条线程去执行收费操作,并不影响车辆通行,等车辆行驶出几百米后,异步的线程执行完毕,短信也发到了车主的手机上。
多加一些思考就能发现,因为速度是相对的,阻塞也是相对的,收费亭A的速度慢,但是对于它自己来说,它已经是全速了,它没停过就没有阻塞,但是高速路到来B的车因为它停下来等待了,所以阻塞须有A和B相互参照,才能看出谁是瓶颈。
同步和异步,也是相对的,这取决于主体的粒度,应用服务里A有100条线程在协同完成任务X,主体为线程时,他们是异步的,但当你把整个服务A看作一个整体时,他是同步的,因为不管你内部有多少线程,你都只是完成了任务X,仅由一个主体,完成一个事务,就是同步。
运用这些思维,可以很好的去理解阻塞队列、线程池、连接池等组件,以后有空再展开吧。
以上就是一篇文章理解阻塞、非阻塞、同步、异步的详细内容,更多关于阻塞、非阻塞、同步、异步的资料请关注我们其它相关文章!