结论
同步和异步是从任务完成后的通知机制来描述任务的概念,而阻塞和非阻塞是从任务提交者的角度来描述任务的。它们只是从不同的维度来描述任务。
另外,回调和同步、异步、阻塞、非阻塞不是一个层次的概念,虽然各种文章经常提到“异步回调”, 但是异步与回调并没有直接的联系,回调只是异步的一种可行的实现方式。当然同步回调也是存在的。
举例
以迅雷下载为例:迅雷下载东西完成时会发出“叮”的一声。
那么根据前面提到的两个维度来排列组合出来可能的任务:
人在观察任务有没有下载完,人作为下载任务的提交者,从人的角度,就有两种可能的方式:
- 人在提交下载任务后,啥都不干,等着任务下载完
- 人在提交下载任务后,去干其他事情,时不时跑来自己看有没有下载完
从任务通知的角度,迅雷在下载文件后会发出“叮”的一声,因此从是否发出“叮”的一声这个角度,也有两种可能的方式:
- 下载完成后,发出“叮”的一声
- 下载完成后,不发出“叮”的一声
把这两种方式进行排列组合,一共有2*2=4
种情况:
- 任务下载时人不可以干其他事情,迅雷不发出声音;
- 任务下载时人不可以干其他事情,迅雷发出声音;
- 任务下载时人可以干其他事情,迅雷不发出声音;
- 任务下载时人可以干其他事情,迅雷发出声音。
同步和异步
迅雷发出声音,就是异步;迅雷不发出声音,就是同步。
到底是同步还是异步,跟人是否等着任务完成没有关系,因为同步异步是从任务完成后的通知机制来描述任务的。只要有通知,就是异步;只要没有通知,就是同步。
阻塞和非阻塞
任务下载时人不可以干其他事情,就是阻塞;任务下载时人可以干其他事情,就是非阻塞。
到底是阻塞还是非阻塞,跟迅雷是否发出声音没有关系,因为阻塞非阻塞是从任务提交者的角度来描述任务的。只要任务提交者在任务未完成时不能干其他事情,就是阻塞;只要能干其他事情,就是非阻塞。
回到例子
还是前面的例子:
-
任务下载时人不可以干其他事情,迅雷不发出声音;(同步,阻塞)
同步体现在:下载完成后迅雷不会发出“叮”的一声通知你任务完成
阻塞体现在:任务下载未完成时,人不可以干其他事情 -
任务下载时人不可以干其他事情,迅雷发出声音;(异步,阻塞)
异步体现在:下载完成后迅雷发出“叮”的一声通知你任务下载完成
阻塞体现在:任务下载未完成时,人不可以干其他事情 -
任务下载时人可以干其他事情,迅雷不发出声音;(同步,非阻塞)
同步体现在:迅雷不会发出声音通知你任务完成
非阻塞体现在:任务下载未完成时,人可以干其他事情 -
任务下载时人可以干其他事情,迅雷发出声音。(异步,非阻塞)
异步体现在:下载完成后迅雷发出“叮”的一声通知你任务下载完成
非阻塞体现在:任务下载未完成时,人可以干其他事情
和 IO 联系
Java中有三种IO:BIO/NIO/AIO
,按照上面的说法:
-
BIO
:同步阻塞磁盘完成IO后会通知内核,这个其实从内核的角度看属于异步;但是从上层API调用的角度还是属于同步,因此这里将它算作同步。
-
NIO
:同步非阻塞 -
AIO
:异步非阻塞
按照排列组合应该有4种情况啊,上面只有3种,仔细一看发现缺少了异步阻塞
,Java类库为什么没有呢?
以前面迅雷下载的例子为例,异步阻塞
相当于:人提交了下载任务给迅雷,任务完成后迅雷会发出声音通知人,但是人提交任务后啥都不干,坐在那里等着迅雷发出声音... (这种行为感觉有点智障吧)。
进一步联系到Linux的传统IO和IO多路复用:
-
read,write,fopen
等这类函数,是同步阻塞,Java-BIO 的 OS 模型; -
select/poll
模型,是同步非阻塞,Java-NIO 的 OS 模型; -
epoll
模型,是异步非阻塞。