每次谈到网络 I/O 的时候,总会出现几个容易令人混淆的概念:同步 / 异步、阻塞 / 非阻塞 的区别。
让我们来盘一盘这几个概念!
同步 / 异步
同步和异步指的是:当前线程是否需要等待方法调用执行完成。
【栗子🌰】调用一个烧水的方法 f:
1)同步指的是调用这个方法 f,当前线程需要等待水烧完,然后得到结果,接着再继续执行剩下的代码逻辑。
// 同步调用 |
2)异步指的是调用这个方法 f,立马就返回,不需要等待水烧完,可以立即执行后续的代码逻辑,然后利用回调或者事件通知的方式得到水烧开的结果。
// 异步调用 |
可以很直观的看出,同步和异步就是调用方式的不同。
需要注意的是,在异步调用的情况下,代码逻辑相对不太直观,通常都需要通过回调方法或者事件通知,在复杂逻辑下对开发者的要求较高。同步调用代码逻辑相对比较直观,等待执行完毕后拿到结果紧接着执行后续的逻辑,不容易出错。
很多方法虽然是异步调用的方式,但是最终的使用还是「异步转同步」。
比如在 Java 中,向线程池提交一个任务,得到一个 future
对象,此时是异步的,然后调用了
future.get()
,那么就变成了同步等待这个任务执行完成。
阻塞 / 非阻塞
阻塞和非阻塞指的是:当前接口数据还没有准备就绪,线程是否被阻塞挂起。
阻塞挂起:当前线程还拥有 CPU 时间片,调用了阻塞的方法,由于数据未准备就绪,当前线程就染出 CPU。
阻塞和同步看起来都是在等待,但是它们本质不一样,同步没有让出 CPU。
非阻塞就是当前接口数据还未准备就绪时,线程不会被阻塞挂起,可以不断轮询请求接口,看看数据是否已经准备就绪。
小结
1)同步和异步:当数据还未处理完成时,代码的逻辑处理方式不同;
2)阻塞和非阻塞:当数据还未处理完成时,线程的状态。
从 I/O 的角度来看:
- 阻塞和非阻塞指的是发起 I/O 请求后,用户线程状态的不同,阻塞I/O在数据未准备就绪的时候会阻塞当前用户线程,而非阻塞 I/O 会立马返回一个结果,不会阻塞当前用户线程。
- 同步和异步是指,内核的 I/O 拷贝实现,当数据准备就绪后,需要将内核空间的数据拷贝至用户空间,如果是同步 I/O 那么用户线程会等待拷贝的完成,而异步 I/O则这个拷贝过程用户线程可以处理其他任务,当内核拷贝完成之后会「通知」用户线程。