目前为止,同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲得这么清楚的好文章很多。没有达到我的预期,所以我自己写了一篇文章。常规的误解是假设需要显示用户详情,分为两步。首先调用HTTP接口获取明细数据,然后使用合适的视图展示明细数据。如果网速很慢,代码发起HTTP请求后就会卡住,直到十几秒后才得到HTTP响应,然后继续执行。这时候如果问别人刚才代码发起的请求是不是同步请求,对方肯定回答是。没错,确实如此。但是你要问这是为什么?对方肯定是这样回答的,“因为发起请求后,代码卡住了,无法继续执行,直到收到响应。”我相信很多人都有同样的想法。其实,这是错误的。它颠倒了因果关系:不是因为代码卡住了才叫同步请求,而是因为是同步请求才叫代码卡住。没有移动。至于为什么会卡死,是由操作系统和CPU决定的:因为内核空间中相应的函数会被卡死,导致用户空间发起的系统调用被卡死,然后用户代码在程序会卡住。它卡住了。所以被卡住只是同步请求的副作用,不能用它来定义同步请求,那怎么定义呢?同步与异步所谓同步,就是步调协调。既然叫协作,至少要有两个以上的东西存在。协同的结果是:多件事情不能同时进行,必须一件一件来,前一件事情结束之后再开始下一件事情。那么当一件事在进行时,其他事情在做什么呢?严格来说并没有这个要求,但一般是处于“等待”状态,因为通常后面的事情的正常进行取决于前面的事情的结果或者前面的事情正在使用的资源。所以,可以认为synchronization要关注的是,从宏观上看,多个事物一个一个序列化,绝对不会有交叉。所以,自然不会去关注某个特定的事物在某个时刻处于什么状态。应用这个理论的最好方法是“排队”。任何资源稀缺、需求高的场景都会用到排队。比如排队买火车票:其实售票大厅更关心的是旅客一个个去窗口买票,因为一次只能卖一张票。就算大家一拥而上,一次也只能卖一张票,何必呢?挤在一起是不安全的。只是有些人素质不好,硬要往上推。售票大厅只好采用排队的形式来达到自己的目的,也就是一张张买票。至于排队时每个乘客的状态,是看手机还是通话,根本不需要关心。除了这种由资源引起的同步之外,还有一种是由逻辑顺序引起的同步。比如先更新代码,再编译,再打包。这些操作只能按照这个顺序一个接一个地执行,因为后一步要用到前一步的结果。关于同步还有两点要知道:一是范围,不需要全局同步,只需要在一些关键点同步。比如食堂卖菜只有一个窗口,必须同步。一个人买完后,下一个人再买。但是吃饭的时候是不是一个人吃完了,下一个人开始吃呢?当然不是。二是粒度。不仅可以同步大粒度的东西,也可以同步小粒度的东西。只是小粒度事物的同步通常是自然支持的,而大粒度事物的同步往往需要人工处理。比如两个线程的同步需要手动处理,但是一个线程中的两条语句自然是同步的。所谓异步,就是步骤不一样。由于它们不同,因此它们不相同。所以结果就是:你做你的,我做我的,谁也不关心谁,一切都在同时进行。一句话,同步就是不能同时启动多个事物,异步就是可以同时启动多个事物。注意:一定要理解“多事物”,多线程是多事物,多方法是多事物,多语句是多事物,多条CPU指令是多事物。等等等等阻塞和非阻塞所谓阻塞,指的是阻碍阻塞。它的本义可以理解为遇到障碍而导致的不动。所谓不阻塞,自然与阻塞相反,可以理解为继续畅通无阻,因为没有遇到任何阻碍。这两个词的解释是,当今中国的一个主要交通问题是堵车:汽车能正常通行,就是不堵车。一旦被挡住,都躺下不动,就被挡住了。所以阻塞重在不能动,非阻塞重在能动。不能动的结果是等待,能动的结果是继续前进。所以匹配blocking的词一定是waiting,匹配non-blocking的词一定是proceed。回到程序中,阻塞也意味着停止等待,非阻塞意味着可以继续向下执行。阻塞和等待等待只是阻塞的副作用,表明随着时间的推移没有任何有意义的事情发生或进展。阻塞的真正含义是你关心的事情由于某种原因无法进行,所以它让你等待。但是没有必要等待,你可以做一些其他不相关的事情,因为这并不影响你等待相关的事情。堵车时,您可以等待。你也可以玩手机,和别人聊天,打牌,甚至先去吃饭。因为这些东西不影响你等堵车。不过,你的车必须停在原地。在计算机中,没有人如此灵活。一般阻塞的时候,选择等待,因为这样最容易实现,挂起线程,让出CPU即可。当条件满足时,线程将被重新调度。所谓两对同步/异步结合,重点在于能否同时启动工作。所谓阻塞/非阻塞,重点在于能不能动。通过推理组合:同步阻塞,不能同时开始工作,不能移动。只有一条小路,一次只能通过一辆车,悲催的被堵死了。同步非阻塞,不能同时启动,但可以移动。只有一条小道,一次只能有一辆车通过,幸好能正常通过。异步阻塞,可以同时开始工作,但是不能移动。路很多,每条路都可以开跑车,可惜都堵死了。异步非阻塞,可在工作时间启动或移动。道路很多,每条道路都可以开跑车,最爽的是都可以正常通行。容易理解吗?其实他们的侧重点是不一样的。只要明白了这一点,将它们结合起来就不是问题了。回到程序,将它们与线程关联起来:同步阻塞相当于一个线程在等待。同步非阻塞相当于一个线程正常运行。异步阻塞意味着有多个线程在等待。异步非阻塞就是多线程正常运行。I/OIO指的是读/写数据的过程,以及等待读/写数据的过程。一旦获取到数据,就变成了数据操作,而不是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。
