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

目录
  • 理解阻塞、非阻塞、同步、异步
    • 阻塞
    • 非阻塞
    • 同步
    • 异步
  • 总结

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

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

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

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

(0)

相关推荐

  • 浅谈socket同步和异步、阻塞和非阻塞、I/O模型

    在进行网络编程时,常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式 同步/异步主要针对C端: 同步:c端发出一个功能调用时,在没有得到结果之前,c端死等结果 例如:普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步:c端一个异步过程调用发出后,调用者不会立刻得到结果.实际处理这个调用的部件在完成后,通过状态.通知和回调来通知调用者. 例如:ajax请求(异步): 事件触发->服务

  • 简述JAVA同步、异步、阻塞和非阻塞之间的区别

    同步和异步,阻塞和非阻塞是大家经常会听到的概念,但是它们是从不同维度来描述一件事情,常常很容易混为一谈. 1. 同步和异步 同步和异步描述的是消息通信的机制. 同步 当一个request发送出去以后,会得到一个response,这整个过程就是一个同步调用的过程.哪怕response为空,或者response的返回特别快,但是针对这一次请求而言就是一个同步的调用. 异步 当一个request发送出去以后,没有得到想要的response,而是通过后面的callback.状态或者通知的方式获得结果.可

  • 科学知识:同步、异步、阻塞和非阻塞区别

    简单点说: 阻塞就是干不完不准回来,一直处于等待中,直到事情处理完成才返回: 非阻塞就是你先干,我先看看有其他事没有,一发现事情被卡住,马上报告领导. 我们拿最常用的send和recv两个函数来说吧... 比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话...这时候就体现出阻塞和非阻塞的不同之处了:

  • 详解socket阻塞与非阻塞,同步与异步、I/O模型

    socket阻塞与非阻塞,同步与异步 1. 概念理解 在进行网络编程时,我们常常见到同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)四种调用方式: 同步/异步主要针对C端: 同步: 所谓同步,就是在c端发出一个功能调用时,在没有得到结果之前,该调用就不返回.也就是必须一件一件事做,等前一件做完了才能做下一件事. 例如普通B/S模式(同步):提交请求->等待服务器处理->处理完毕返回 这个期间客户端浏览器不能干任何事 异步: 异步的概念和同步相对.当c端一个异步

  • java 中同步、异步、阻塞和非阻塞区别详解

    java 中同步.异步.阻塞和非阻塞区别详解 简单点说: 阻塞就是干不完不准回来,一直处于等待中,直到事情处理完成才返回: 非阻塞就是你先干,我先看看有其他事没有,一发现事情被卡住,马上报告领导. 我们拿最常用的send和recv两个函数来说吧... 比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话

  • java 同步、异步、阻塞和非阻塞分析

    java 同步.异步.阻塞和非阻塞分析 概要: 正常情况下,我们的程序以同步非阻塞的方式在运行.但是我们的程序总会出现一些耗时操作,比如复杂的计算(找出1到10亿之间的素数)和程序本身无法控制的操作(IO操作.网络请求).包含这些耗时操作的方法我们可以把它称为阻塞方法,包含这些耗时操作的任务我们可以把它称为阻塞任务.阻塞与非阻塞是以是否耗时来定义的. 如果程序中存在大量阻塞操作,就会影响程序性能.但是阻塞的存在是客观事实,我们的程序是无法改变它的,一个网络请求需要3秒才能响应,我们不可能让它1毫

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

    目录 理解阻塞.非阻塞.同步.异步 阻塞 非阻塞 同步 异步 总结 理解阻塞.非阻塞.同步.异步 首先说明,这些都是在特点场景下或者相对情况的词汇,OK,接下来开门见山. 阻塞 可以很直观的理解,就如节假日高速路出口收费站一样,上图片: 9个收费亭,同时来了一大波车,这时候同一时刻只能有9辆车在收费,剩下的车都在只能在后面排队等待,这就是现实中很直观的阻塞现象.这9个收费亭,就是一个瓶颈,或许画为这样更符合大家对瓶颈二字的理解: 第1张图中,高速公路源源不断的车辆到来,和第二张图的效果其实表示一

  • 处理java异步事件的阻塞和非阻塞方法分析

    前言 由于多核系统普遍存在,并发性编程的应用无疑比以往任何时候都要广泛.但并发性很难正确实现,用户需要借助新工具来使用它.很多基于 JVM 的语言都属于这类开发工具,Scala 在这一领域尤为活跃.本系列文章将介绍一些针对 Java 和 Scala 语言的较新的并发性编程方法. 在任何并发性应用程序中,异步事件处理都至关重要.事件来源可能是不同的计算任务.I/O 操作或与外部系统的交互.无论来源是什么,应用程序代码都必须跟踪事件,协调为响应事件而采取的操作. Java 应用程序可采用两种基本的异

  • Flask实现异步非阻塞请求功能实例解析

    本文研究的主要是Flask实现异步非阻塞请求功能,具体实现如下. 最近做物联网项目的时候需要搭建一个异步非阻塞的HTTP服务器,经过查找资料,发现可以使用gevent包. 关于gevent Gevent 是一个 Python 并发网络库,它使用了基于 libevent 事件循环的 greenlet 来提供一个高级同步 API.下面是代码示例: from gevent.wsgi import WSGIServer from yourapplication import app http_serve

  • 学习非阻塞的同步机制CAS

    在研究线程池的执行原理时,看到一段不断循环重试的代码,不理解它的原理,看注释这是CAS的实现,所以学会之后记录下来. 锁有什么劣势 在多线程并发下,可以通过加锁来保证线程安全性,但多个线程同时请求锁,很多情况下避免不了要借助操作系统,线程挂起和恢复会存在很大的开销,并存在很长时间的中断.一些细粒度的操作,例如同步容器,操作往往只有很少代码量,如果存在锁并且线程激烈地竞争,调度的代价很大. 总结来说,线程持有锁,会让其他需要锁的线程阻塞,产生多种风险和开销.加锁是一种悲观方法,线程总是设想在自己持

随机推荐