一篇文章搞懂同步、异步、阻塞、非阻塞、BIO、NIO和AIO

常规的误区假设有一个展示用户详情的需求 , 分两步 , 先调用一个HTTP接口拿到详情数据 , 然后使用适合的视图展示详情数据 。
如果网速很慢 , 代码发起一个HTTP请求后 , 就卡住不动了 , 直到十几秒后才拿到HTTP响应 , 然后继续往下执行 。
这个时候你问别人 , 刚刚代码发起的这个请求是不是一个同步请求 , 对方一定回答是 。 这是对的 , 它确实是 。
但你要问它为什么是呢?对方一定是这样回答的 , “因为发起请求后 , 代码就卡住不动了 , 直到拿到响应后才可以继续往下执行” 。
我相信很多人也都是这样认为的 , 其实这是不对的 , 是把因果关系搞反了:
不是因为代码卡住不动了才叫同步请求 , 而是因为它是同步请求所以代码才卡住不动了 。
至于为什么能卡住不动 , 这是由操作系统和CPU决定的:
因为内核空间里的对应函数会卡住不动 , 造成用户空间发起的系统调用卡住不动 , 继而使程序里的用户代码卡住不动了 。
因此卡住不动了只是同步请求的一个副作用 , 并不能用它来定义同步请求 , 那该如何定义呢?
同步和异步【一篇文章搞懂同步、异步、阻塞、非阻塞、BIO、NIO和AIO】所谓同步 , 指的是协同步调 。 既然叫协同 , 所以至少要有2个以上的事物存在 。 协同的结果就是:
多个事物不能同时进行 , 必须一个一个的来 , 上一个事物结束后 , 下一个事物才开始 。
那当一个事物正在进行时 , 其它事物都在干嘛呢?
严格来讲这个并没有要求 , 但一般都是处于一种“等待”的状态 , 因为通常后面事物的正常进行都需要依赖前面事物的结果或前面事物正在使用的资源 。
因此 , 可以认为 , 同步更希望关注的是从宏观整体来看 , 多个事物是一种逐个逐个的串行化关系 , 绝对不会出现交叉的情况 。
所以 , 自然也不太会去关注某个瞬间某个具体事物是处于一个什么状态 。
把这个理论应用的出神入化的非“排队”莫属 。 凡是在资源少需求多的场景下都会用到排队 。
比如排队买火车票这件事:
其实售票大厅更在意的是旅客一个一个的到窗口去买票 , 因为一次只能卖一张票 。
即使大家一窝蜂的都围上去 , 还是一次只能卖一张票 , 何必呢?挤在一起又不安全 。
只是有些人素质太差 , 非要往上挤 , 售票大厅迫不得已 , 采用排队这种形式来达到自己的目的 , 即一个一个的买票 。
至于每个旅客排队时的状态 , 是看手机呀还是说话呀 , 根本不用去在意 。
除了这种由于资源导致的同步外 , 还存在一种由于逻辑上的先后顺序导致的同步 。
比如 , 先更新代码 , 然后再编译 , 接着再打包 。 这些操作由于后一步要使用上一步的结果 , 所以只能按照这种顺序一个一个的执行 。
关于同步还需知道两个小的点:
一是范围 , 并不需要在全局范围内都去同步 , 只需要在某些关键的点执行同步即可 。
比如食堂只有一个卖饭窗口 , 肯定是同步的 , 一个人买完 , 下一个人再买 。 但吃饭的时候也是一个人吃完 , 下一个人才开始吃吗?当然不是啦 。
二是粒度 , 并不是只有大粒度的事物才有同步 , 小粒度的事物也有同步 。
只不过小粒度的事物同步通常是天然支持的 , 而大粒度的事物同步往往需要手工处理 。
比如两个线程的同步就需要手工处理 , 但一个线程里的两个语句天然就是同步的 。
所谓异步 , 就是步调各异 。 既然是各异 , 那就是都不相同 。 所以结果就是:
多个事物可以你进行你的、我进行我的 , 谁都不用管谁 , 所有的事物都在同时进行中 。
一言以蔽之 , 同步就是多个事物不能同时开工 , 异步就是多个事物可以同时开工 。
注:一定要去体会“多个事物” , 多个线程是多个事物 , 多个方法是多个事物 , 多个语句是多个事物 , 多个CPU指令是多个事物 。 等等等等 。 阻塞和非阻塞所谓阻塞 , 指的是阻碍堵塞 。 它的本意可以理解为由于遇到了障碍而造成的动弹不得 。
所谓非阻塞 , 自然是和阻塞相对 , 可以理解为由于没有遇到障碍而继续畅通无阻 。
对这两个词最好的诠释就是 , 当今中国一大交通难题 , 堵车:
汽车可以正常通行时 , 就是非阻塞 。 一旦堵上了 , 全部趴窝 , 一动不动 , 就是阻塞 。
因此阻塞关注的是不能动 , 非阻塞关注的是可以动 。
不能动的结果就是只能等待 , 可以动的结果就是继续前行 。