原文连接:http://www.javashuo.com/article/p-gpkwzcyn-er.htmlhtml
常规的误区
假设有一个展现用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,而后使用适合的视图展现详情数据。
若是网速很慢,代码发起一个HTTP请求后,就卡住不动了,直到十几秒后才拿到HTTP响应,而后继续往下执行。
这个时候你问别人,刚刚代码发起的这个请求是否是一个同步请求,对方必定回答是。这是对的,它确实是。
但你要问它为何是呢?对方必定是这样回答的,“由于发起请求后,代码就卡住不动了,直到拿到响应后才能够继续往下执行”。
我相信不少人也都是这样认为的,其实这是不对的,是把因果关系搞反了:
不是由于代码卡住不动了才叫同步请求,而是由于它是同步请求因此代码才卡住不动了。
至于为何能卡住不动,这是由操做系统和CPU决定的:
由于内核空间里的对应函数会卡住不动,形成用户空间发起的系统调用卡住不动,继而使程序里的用户代码卡住不动了。
所以卡住不动了只是同步请求的一个反作用,并不能用它来定义同步请求,那该如何定义呢?
同步和异步
所谓同步,指的是协同步调。既然叫协同,因此至少要有2个以上的事物存在。协同的结果就是:
多个事物不能同时进行,必须一个一个的来,上一个事物结束后,下一个事物才开始。
那当一个事物正在进行时,其它事物都在干吗呢?
严格来说这个并无要求,但通常都是处于一种“等待”的状态,由于一般后面事物的正常进行都须要依赖前面事物的结果或前面事物正在使用的资源。
所以,能够认为,同步更但愿关注的是从宏观总体来看,多个事物是一种逐个逐个的串行化关系,绝对不会出现交叉的状况。
因此,天然也不太会去关注某个瞬间某个具体事物是处于一个什么状态。
把这个理论应用的出神入化的非“排队”莫属。凡是在资源少需求多的场景下都会用到排队。
好比排队买火车票这件事:
其实售票大厅更在乎的是旅客一个一个的到窗口去买票,由于一次只能卖一张票。
即便你们一窝蜂的都围上去,仍是一次只能卖一张票,何须呢?挤在一块儿又不安全。
只是有些人素质太差,非要往上挤,售票大厅无可奈何,采用排队这种形式来达到本身的目的,即一个一个的买票。
至于每一个旅客排队时的状态,是看手机呀仍是说话呀,根本不用去在乎。
安全
除了这种因为资源致使的同步外,还存在一种因为逻辑上的前后顺序致使的同步。
好比,先更新代码,而后再编译,接着再打包。这些操做因为后一步要使用上一步的结果,因此只能按照这种顺序一个一个的执行。
关于同步还需知道两个小的点:
一是范围,并不须要在全局范围内都去同步,只须要在某些关键的点执行同步便可。
好比食堂只有一个卖饭窗口,确定是同步的,一我的买完,下一我的再买。但吃饭的时候也是一我的吃完,下一我的才开始吃吗?固然不是啦。
二是粒度,并非只有大粒度的事物才有同步,小粒度的事物也有同步。网络
只不太小粒度的事物同步一般是自然支持的,而大粒度的事物同步每每须要手工处理。
好比两个线程的同步就须要手工处理,但一个线程里的两个语句自然就是同步的。
所谓异步,就是步调各异。既然是各异,那就是都不相同。因此结果就是:
多个事物能够你进行你的、我进行个人,谁都不用管谁,全部的事物都在同时进行中。异步
一言以蔽之,同步就是多个事物不能同时开工,异步就是多个事物能够同时开工。函数
注:必定要去体会“多个事物”,多个线程是多个事物,多个方法是多个事物,多个语句是多个事物,多个CPU指令是多个事物。等等等等。
spa
阻塞和非阻塞操作系统
所谓阻塞,指的是阻碍堵塞。它的本意能够理解为因为遇到了障碍而形成的动弹不得。线程
所谓非阻塞,天然是和阻塞相对,能够理解为因为没有遇到障碍而继续畅通无阻。
对这两个词最好的诠释就是,当今中国一大交通难题,堵车:
汽车能够正常通行时,就是非阻塞。一旦堵上了,所有趴窝,一动不动,就是阻塞。htm
所以阻塞关注的是不能动,非阻塞关注的是能够动。blog
不能动的结果就是只能等待,能够动的结果就是继续前行。
所以和阻塞搭配的词必定是等待,和非阻塞搭配的词必定是进行。
回到程序里,阻塞一样意味着停下来等待,非阻塞代表能够继续向下执行。
阻塞和等待
等待只是阻塞的一个反作用而已,代表随着时间的流逝,没有任何有意义的事物发生或进行。
阻塞的真正含义是你关心的事物因为某些缘由没法继续进行,所以让你等待。但不必干等,你能够作一些其它无关的事物,由于这并不影响你对相关事物的等待。
在堵车时,你能够干等。也能够玩手机、和别人聊天,或者打牌、甚至先去吃饭都行。由于这些事物并不影响你对堵车的等待。不过你的车必须呆在原地。
在计算机里,是没有人这么灵活的,通常在阻塞时,选在干等,由于这最容易实现,只须要挂起线程,让出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,由于它值得专门撰文一篇。