关于异步,同步,阻塞,非阻塞的理解(转载)

2023-05-16

常规的误区


假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使用适合的视图展示详情数据。

如果网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTTP响应,然后继续往下执行。

这个时候你问别人,刚刚代码发起的这个请求是不是一个同步请求,对方一定回答是。这是对的,它确实是。

但你要问它为什么是呢?对方一定是这样回答的,“因为发起请求后,代码就卡住不动了,直到拿到响应后才可以继续往下执行”。

我相信很多人也都是这样认为的,其实这是不对的,是把因果关系搞反了:

不是因为代码卡住不动了才叫同步请求,而是因为它是同步请求所以代码才卡住不动了。

至于为什么能卡住不动,这是由操作系统和CPU决定的:

因为内核空间里的对应函数会卡住不动,造成用户空间发起的系统调用卡住不动,继而使程序里的用户代码卡住不动了。

因此卡住不动了只是同步请求的一个副作用,并不能用它来定义同步请求,那该如何定义呢?


同步和异步


所谓同步,指的是协同步调。既然叫协同,所以至少要有2个以上的事物存在。协同的结果就是:

多个事物不能同时进行,必须一个一个的来,上一个事物结束后,下一个事物才开始。

那当一个事物正在进行时,其它事物都在干嘛呢?

严格来讲这个并没有要求,但一般都是处于一种“等待”的状态,因为通常后面事物的正常进行都需要依赖前面事物的结果或前面事物正在使用的资源。

因此,可以认为,同步更希望关注的是从宏观整体来看,多个事物是一种逐个逐个的串行化关系,绝对不会出现交叉的情况。

所以,自然也不太会去关注某个瞬间某个具体事物是处于一个什么状态。

把这个理论应用的出神入化的非“排队”莫属。凡是在资源少需求多的场景下都会用到排队。

比如排队买火车票这件事:

其实售票大厅更在意的是旅客一个一个的到窗口去买票,因为一次只能卖一张票。

即使大家一窝蜂的都围上去,还是一次只能卖一张票,何必呢?挤在一起又不安全。

只是有些人素质太差,非要往上挤,售票大厅迫不得已,采用排队这种形式来达到自己的目的,即一个一个的买票。

至于每个旅客排队时的状态,是看手机呀还是说话呀,根本不用去在意。
 

除了这种由于资源导致的同步外,还存在一种由于逻辑上的先后顺序导致的同步。

比如,先更新代码,然后再编译,接着再打包。这些操作由于后一步要使用上一步的结果,所以只能按照这种顺序一个一个的执行。

关于同步还需知道两个小的点:

一是范围,并不需要在全局范围内都去同步,只需要在某些关键的点执行同步即可。

比如食堂只有一个卖饭窗口,肯定是同步的,一个人买完,下一个人再买。但吃饭的时候也是一个人吃完,下一个人才开始吃吗?当然不是啦。

二是粒度,并不是只有大粒度的事物才有同步,小粒度的事物也有同步。

 

只不过小粒度的事物同步通常是天然支持的,而大粒度的事物同步往往需要手工处理。

比如两个线程的同步就需要手工处理,但一个线程里的两个语句天然就是同步的。

所谓异步,就是步调各异。既然是各异,那就是都不相同。所以结果就是:

多个事物可以你进行你的、我进行我的,谁都不用管谁,所有的事物都在同时进行中。

 

一言以蔽之,同步就是多个事物不能同时开工,异步就是多个事物可以同时开工。

 

注:一定要去体会“多个事物”,多个线程是多个事物,多个方法是多个事物,多个语句是多个事物,多个CPU指令是多个事物。等等等等。

 

 

阻塞和非阻塞

 

 

所谓阻塞,指的是阻碍堵塞。它的本意可以理解为由于遇到了障碍而造成的动弹不得。

 

所谓非阻塞,自然是和阻塞相对,可以理解为由于没有遇到障碍而继续畅通无阻。

对这两个词最好的诠释就是,当今中国一大交通难题,堵车:

汽车可以正常通行时,就是非阻塞。一旦堵上了,全部趴窝,一动不动,就是阻塞。

 

因此阻塞关注的是不能动,非阻塞关注的是可以动。

 

不能动的结果就是只能等待,可以动的结果就是继续前行。

因此和阻塞搭配的词一定是等待,和非阻塞搭配的词一定是进行。

回到程序里,阻塞同样意味着停下来等待,非阻塞表明可以继续向下执行。


阻塞和等待

 

 

等待只是阻塞的一个副作用而已,表明随着时间的流逝,没有任何有意义的事物发生或进行。

 

阻塞的真正含义是你关心的事物由于某些原因无法继续进行,因此让你等待。但没必要干等,你可以做一些其它无关的事物,因为这并不影响你对相关事物的等待。

在堵车时,你可以干等。也可以玩手机、和别人聊天,或者打牌、甚至先去吃饭都行。因为这些事物并不影响你对堵车的等待。不过你的车必须呆在原地。

在计算机里,是没有人这么灵活的,一般在阻塞时,选在干等,因为这最容易实现,只需要挂起线程,让出CPU即可。在条件满足时,会重新调度该线程。


两两组合


所谓同步/异步,关注的是能不能同时开工。

所谓阻塞/非阻塞,关注的是能不能动。

通过推理进行组合:

同步阻塞,不能同时开工,也不能动。只有一条小道,一次只能过一辆车,可悲的是还TMD的堵上了。

同步非阻塞,不能同时开工,但可以动。只有一条小道,一次只能过一辆车,幸运的是可以正常通行。

异步阻塞,可以同时开工,但不可以动。有多条路,每条路都可以跑车,可气的是全都TMD的堵上了。

异步非阻塞,可以工时开工,也可以动。有多条路,每条路都可以跑车,很爽的是全都可以正常通行。

是不是很容易理解啊。其实它们的关注点是不同的,只要搞明白了这点,组合起来也不是事儿。

回到程序里,把它们和线程关联起来:

同步阻塞,相当于一个线程在等待。

同步非阻塞,相当于一个线程在正常运行。

异步阻塞,相当于多个线程都在等待。

异步非阻塞,相当于多个线程都在正常运行。

 

 

I/O

 

 

IO指的就是读入/写出数据的过程,和等待读入/写出数据的过程。一旦拿到数据后就变成了数据操作了,就不是IO了。

拿网络IO来说,等待的过程就是数据从网络到网卡再到内核空间。读写的过程就是内核空间和用户空间的相互拷贝。

 

所以IO就包括两个过程,一个是等待数据的过程,一个是读写(拷贝)数据的过程。而且还要明白,一定能包括操作数据的过程。

 

 

阻塞IO和非阻塞IO

 

 

应用程序都是运行在用户空间的,所以它们能操作的数据也都在用户空间。按照这样子来理解,只要数据没有到达用户空间,用户线程就操作不了。

 

如果此时用户线程已经参与,那它一定会被阻塞在IO上。这就是常说的阻塞IO。用户线程被阻塞在等待数据上或拷贝数据上。

 

非阻塞IO就是用户线程不参与以上两个过程,即数据已经拷贝到用户空间后,才去通知用户线程,一上来就可以直接操作数据了。

 

用户线程没有因为IO的事情出现阻塞,这就是常说的非阻塞IO。

 

 

同步IO和同步阻塞IO

 

 

按照上文中对同步的理解,同步IO是指发起IO请求后,必须拿到IO的数据才可以继续执行。

按照程序的表现形式又分为两种:

在等待数据的过程中,和拷贝数据的过程中,线程都在阻塞,这就是同步阻塞IO。

在等待数据的过程中,线程采用死循环式轮询,在拷贝数据的过程中,线程在阻塞,这其实还是同步阻塞IO。

 

网上很多文章把第二种归为同步非阻塞IO,这肯定是错误的,它一定是阻塞IO,因为拷贝数据的过程,线程是阻塞的。

严格来讲,在IO的概念上,同步和非阻塞是不可能搭配的,因为它们是一对相悖的概念。

同步IO意味着必须拿到IO的数据,才可以继续执行。因为后续操作依赖IO数据,所以它必须是阻塞的。

非阻塞IO意味着发起IO请求后,可以继续往下执行。说明后续执行不依赖于IO数据,所以它肯定不是同步的。

因此,在IO上,同步和非阻塞是互斥的,所以不存在同步非阻塞IO。但同步非阻塞是存在的,那不叫IO,叫操作数据了。

 

所以,同步IO一定是阻塞IO,同步IO也就是同步阻塞IO。

 

 

异步IO和异步阻塞/非阻塞IO

 

 

按照上文中对异步的理解,异步IO是指发起IO请求后,不用拿到IO的数据就可以继续执行。

 

用户线程的继续执行,和操作系统准备IO数据的过程是同时进行的,因此才叫做异步IO。

按照IO数据的两个过程,又可以分为两种:

在等待数据的过程中,用户线程继续执行,在拷贝数据的过程中,线程在阻塞,这就是异步阻塞IO。

在等待数据的过程中,和拷贝数据的过程中,用户线程都在继续执行,这就是异步非阻塞IO。

 

第一种情况是,用户线程没有参与数据等待的过程,所以它是异步的。但用户线程参与了数据拷贝的过程,所以它又是阻塞的。合起来就是异步阻塞IO。

第二种情况是,用户线程既没有参与等待过程也没有参与拷贝过程,所以它是异步的。当它接到通知时,数据已经准备好了,它没有因为IO数据而阻塞过,所以它又是非阻塞的。合起来就是异步非阻塞IO。

 

PS:聪明的你或许发现了我没有提多路复用IO,因为它值得专门撰文一篇。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于异步,同步,阻塞,非阻塞的理解(转载) 的相关文章

  • 解决Count and Say

    一 题目 xff1a The count and say sequence is the sequence of integers with the first five terms as following 1 1 2 11 3 21 4
  • 大数据概述

    一 大数据定义 量大 复杂 二 四个特征以及应用场景 三 大数据技术 1 展现与交互 报表 图形 可视化工具 增强现实 2 数据计算 查询 统计 分析 预测 挖掘 图谱 BI 3 数据存储 分布式文件系统 xff0c 分布式数据库 4 数据
  • hdfs的理解以及shell命令

    一 hdfs的实现思想 xff1a 1 hdfs是利用分布式集群来存储文件的 xff0c 为客户端提供一个便捷的访问方式 xff0c 就是一个虚拟的目录结构 2 文件存储的时候是被分割成若干的block块的 3 文件的bloc块存放在若干台
  • js中的prop()和attr()方法

    以下两种方法等价 xff1a span class token operator lt span input id span class token operator 61 span span class token string 34 t
  • iOS富文本(NSAttributedString)---尽力弄全了

    把简书文章搬过来 最近浮躁 xff0c 毛线都没写 xff0c 不断有人关注点赞我 xff0c 必须总结点干货了 项目上要加载html格式的文本 xff0c 学习一下富文本相关内容 1 加载HTML标签文本 因为解析的数据里面有html标签
  • $ is not defined之SpringMVC中关于jsp中的ajax连接不到controller的问题

    刚刚写完jsp中的ajax xff0c 发现Controller路径名称以及取得的参数取得都正确 xff0c 文本域中的触发函数也正确 xff0c 可就是触发onblur方法的时候 xff0c 发现Controller并没有反应 经过调试后
  • codem2018年初赛A轮

    第六题 小美创建了一套算法 xff0c 第一行输入两个整数 xff0c a和b xff0c 第二行输入一个字符串c 假如a b的小数部分中包含第三个输入的数c xff0c 则输出c在小数部分出现的位置k xff0c 如果不包含 xff0c
  • 接口和抽象类的区别

    在interface里面的变量都是public static final 的 所以你可以这样写 xff1a public static final int i 61 10 或则 int i 61 10 xff1b xff08 可以省略掉一部
  • 动态代理

    动态代理是在不改变原来方法的代码的前提下 xff0c 用来增强原来方法的功能的 在程序的角度上来说 xff0c 就是说让别人来帮助自己完成更加强的功能 xff0c 别人就是动态代理对象 java中的动态代理由两个核心的组件来完成 xff0c
  • 数据库的读写分离和负载均衡

    mysql的数据库读写分离是为了要解决如何在复制集群的不同角色上 xff0c 去执行不同的sql语句 读尽量分布到从服务器上 xff0c 写只能在主服务器上 读的负载均衡则是解决如何在相同的从服务器上分担相同的sql语句的问题 读的负载均衡
  • NIO,BIO,AIO的区别和联系

    一个IO操作其实分成了两个步骤 xff1a 发起IO请求和实际的IO操作 IO操作可以分为3类 xff1a 同步阻塞 xff08 即早期的IO操作 xff09 同步非阻塞 xff08 NIO xff09 异步 xff08 AIO xff09
  • springmvc中文件的上传和下载

    步骤 xff1a 1 在前端的form表单中申明enctype 61 34 multipart form data 34 2 在前端的文件区域设置yourfile lt input type 61 34 file 34 name 61 34
  • git的使用

    管理员从master分支创建develop分支用于开发 git checkout b develop RD从远程仓库pull最新的develop分支 xff0c 并拉个feature分支用于需求开发 git pull origin deve
  • JAVA内存模型剖析

    java内存模型剖析 xff1a A xff1a 内存模型是什么鬼 xff1a 一 cpu和缓存一致性 计算机中cpu要和数据打交道 xff0c 而数据往往是放到主存中去的 xff0c 所以就可以理解成cpu和主存打交道 随着cpu的不断优
  • mybatis学习

    mybatis学习 xff1a 一 jdbc存在的问题 xff1a 1 频繁创建链接 xff0c 造成系统资源的浪费 2 sql语句死板 xff0c 造成sql语句硬编码的问题 3 代码太繁琐 xff0c 维护难 二 mybatis学习改进
  • mysql中Access denied(using password:NO)问题解决

    我使用mysql启动命令启动了mysql后发现了下面错误 这个错误是因为前期我没有对数据库设置密码 xff0c 这里进入mysql报了错误 经过网上查找 xff0c 下面的步骤成功解决了我的问题 在安装mysql的文件夹下找到my int这
  • cas解析

    JAVA CAS原理 unsafe AQS concurrent包的实现 由于java的CAS同时具有 volatile 读和volatile写的内存语义 xff0c 因此Java线程之间的通信现在有了下面四种方式 xff1a A线程写vo
  • 深入理解四种数据库索引类型(- 唯一索引/非唯一索引 - 主键索引(主索引) - 聚集索引/非聚集索引 - 组合索引)

    深入理解四种数据库索引类型 xff08 唯一索引 非唯一索引 主键索引 xff08 主索引 xff09 聚集索引 非聚集索引 组合索引 xff09 唯一索引 非唯一索引主键索引 xff08 主索引 xff09 聚集索引 非聚集索引组合索引
  • linux常用的命令

    一 系统命令 1 pwd 获取到当前的路径 2 su u 切换到管理员 二 文件操作命令 1 文件 tail 查看 rm rf 删除 vi 修改 2 文件夹 mkdir p rm r
  • redis

    1 redis是什么 xff1f Redis是一个key value的数据库 xff0c 先存到内存中去 xff0c 会根据一定的策略持久化到磁盘中去 xff0c 即使断电也不会丢失数据 支持的类型比较多 是内存级别的缓存 常用来作为中央缓

随机推荐

  • 爱奇艺2018年秋招

    清雨又在吃自助餐了 排在清雨面前的有N种食物 xff0c 排成一排 xff0c 清雨可以选择其中的若干种食物 xff0c 但是不能连续选择相邻的食物 因为清雨很挑食 xff0c 当所有食物都不合口味时 xff0c 他可以一种都不选 xff0
  • 解决公司内部pom文件不能访问外部中央仓库的问题

    那这个时候 xff0c 赶紧去指定的settings xml文件添加mirror地址 xff08 经测试 xff0c http repo2 maven org maven2 可用 xff09 xff1a lt mirror gt lt id
  • mybatis工程遇到的问题

    一 mybatis逆向工程运行成功却没有生成相应的包和文件 1 解决办法 原因 xff1a 逆向工程中的路径问题 xff0c windows和mac等的文件系统路径不同 mac和Linux下应该使用 xff0c windows下应该使用
  • spring和springmvc容器的关系

    spring容器是springmvc的父容器 本着父容器不可访问子容器中父容器没有的内容 xff0c 子容器可以访问父容器中有的内容 xff0c 所以在配置扫描包的时候 xff0c spring容器可以扫描到dao xff0c servic
  • 实践宝典

    Mac下查看已安装的jdk版本及其安装目录 xff1a https blog csdn net caoxiaohong1005 article details 73611424 如何将List集合中相同属性的对象合并 xff1a https
  • 锁对象,无锁,偏向锁,轻量级锁,重量级锁

    1 对象的hashcode和hashcode 返回的值是否是一回事 应该是一回事 xff0c 我的理解就是 xff0c 这个hashcode是在对象无锁的状态下标记的 xff0c Java类 xff0c 在被JVM加载的时候 xff0c J
  • IDEA显示当前类中所有的方法列表

  • 搜狐畅游2019校招笔试题-游戏开发工程师(java)

    题目描述 xff1a 一组无序的自然数集合 xff0c 由0 xff0c 1 xff0c 2 xff0c xff0c xff0c xff0c n的数字和一个的数字X组成 xff0c 请从集合中找出这个重复的数字X 例子 xff1a 输入 x
  • 毕业设计

    1 搭建eclipse xff0c 思考基本功能实现 基本功能 xff1a 2 考虑用不用maven xff0c 导jar包容易一些 3 前后端交互 xff0c xff08 登陆 xff0c 注册 xff0c xff0c xff0c xff
  • 今日头条面试

    问题 xff1a 矿泉水1块钱1瓶 xff0c 喝完以后 xff0c 2个空瓶子可以换一瓶新矿泉水 问 xff1a 花10块钱最后最多能得多少瓶矿泉水 解答 xff1a public class Main public static voi
  • 将mac os 中的mysql 彻底删除

    执行下列命令 sudo rm usr local mysqlsudo rm rf usr local mysql sudo rm rf Library StartupItems MySQLCOMsudo rm rf Library Pref
  • MarkDown的使用

    标题 在需要的文字前增加 以及一个空格 一级标题 二级标题 效果 xff1a 一级标题 二级标题 列表 无序列表加 xff0c 有序列表加1 列表 列表 列表 1 列表 1 列表 2 列表 效果 xff1a 列表 列表 列表 列表 列表 列
  • css,html,js实用锦囊

    一 好看的按钮 lt DOCTYPE html gt lt html gt lt head gt lt meta charset 61 34 utf 8 34 gt lt title gt HTML CSS Exercise CSS3 bu
  • VMware虚拟机centos克隆完之后网卡eth0的配置以及主机名的配置

    配置完这些就可以了 第一 配置主机名 vim etc hostname 修改主机名 hadoop4 第二 配置网卡的MAC地址 vi etc udev rules d 70 persistent net rules 修改成如下的内容 SUB
  • 启动zookeeper,但是状态显示报错:Error contacting service. It is probably not running

    问题描述 xff1a 安装zookeeper 3 4 10的时候 xff0c 启动正常没报错 xff0c 但zkServer sh status查看状态的时候却出现错误 xff0c 如下 xff1a ZooKeeper JMX enable
  • MySql优化-count(*)和count(列)哪一个更加快

    MySql优化 count 和count 列 哪一个更加快 1 count 列 count 列 的速度是看列的偏移量来决定的 xff0c 理论上 xff0c 越靠前的列速度越快 xff0c 越靠后的列素的越慢 2 count count 的
  • 测试-Mockito的使用

    一 Mockito简述 Mockito的工作原理是通过创建依赖对象的proxy xff0c 所有的调用先经过proxy对象 xff0c proxy对象拦截了所有的请求再根据预设的返回值进行处理 Mockito包依赖 xff1a lt dep
  • Vue+SpringBoot使用注解@CrossOrigin解决跨域问题

    背景 xff1a 前台vue使用本地8082端口 xff0c 后台使用8080端口 xff0c 这样前台访问后台时候就产生了跨域问题 这里是从后台解决跨域问题 span class token annotation punctuation
  • vmware虚拟机和centos连接不上

    1 VM网络设置 点击NAT设置 记住网关和子网ip xff0c 后面会用 2 CentOs网络设置 root 64 localhost download cd etc sysconfig network scripts root 64 l
  • 关于异步,同步,阻塞,非阻塞的理解(转载)

    常规的误区 假设有一个展示用户详情的需求 xff0c 分两步 xff0c 先调用一个HTTP接口拿到详情数据 xff0c 然后使用适合的视图展示详情数据 如果网速很慢 xff0c 代码发起一个HTTP请求后 xff0c 就卡住不动了 xff