欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

一篇文章理解阻塞、非阻塞、同步、异步

程序员文章站 2022-06-26 09:27:08
目录理解阻塞、非阻塞、同步、异步首先说明,这些都是在特点场景下或者相对情况的词汇,ok,接下来开门见山。阻塞可以很直观的理解,就如节假日高速路出口收费站一样,上图片:9个收费亭,同时来了一大波车,这时...

理解阻塞、非阻塞、同步、异步

首先说明,这些都是在特点场景下或者相对情况的词汇,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,仅由一个主体,完成一个事务,就是同步

运用这些思维,可以很好的去理解阻塞队列、线程池、连接池等组件,以后有空再展开吧。

以上就是一篇文章理解阻塞、非阻塞、同步、异步的详细内容,更多关于阻塞、非阻塞、同步、异步的资料请关注其它相关文章!