目录
一、阻塞非阻塞
1.1阻塞非阻塞针对调用方:
调用方在被调用方准备好之前,调用方是在干等着,还是在做别的,如果调用方干等着就是阻塞,如果调用方边等边做别的就是非阻塞。
举例来说
- 比如A调用B,A是调用方,B是被调用方,A在调用B后,A一直等着B即为阻塞,
- A边等着B,边干点别的则为非阻塞
二、同步与异步:
2.1同步异步针对被调用方:
被调用方是否主动通知调用方,如果是被调用方准备好了,通知调用方,则是异步,如果不通知,则是同步。
举例来说
- 比如A调用B,A不停询问B或者A一直等待着B,B是被动的,则为同步
- B准备好了,B通知A,则为异步
三、阻塞非阻塞同步异步举例
3.1同步阻塞 :
你去买奶茶,店员做奶茶,你坐着等,干等着,什么也不干,这就是同步阻塞
3.2同步非阻塞:
买奶茶付款后,你坐着等,边等边玩手机或边等边聊天,过一会儿去看看显示器或问问店员,我的奶茶做好了吗?这就是同步非阻塞
3.3异步非阻塞:
由于该卖家生意特火爆,你又非想喝这家奶茶,人太多了,做奶茶大概一个小时,店员说你先去逛逛,奶茶好了可以从小程序上看,买奶茶付款后,你去逛,奶茶好了微信小程序给你推送消息,你的奶茶好了,并附上取奶茶号码,你出示号码去取奶茶。
3.4异步阻塞:
一般没有这一说,因为异步就是为了解决阻塞的!
四、I/O 流的阻塞与非阻塞
4.1阻塞 I/O:
- 调用方发起 I/O 操作后,必须等待操作完成才能继续。比如,程序在读取文件内容时,直到文件内容完全读取完毕,程序才会继续执行其他代码。
4.2非阻塞 I/O:
- 调用方发起 I/O 操作后,不需要等待操作完成,可以继续执行其他代码。比如,程序在发起网络请求后,不需要等待服务器响应,可以去处理其他任务,稍后再检查请求是否完成。
五、I/O 流的同步与异步
5.1同步 I/O:
- I/O 操作完成后,被调用方不会主动通知调用方,所以调用方必须自己主动去检查结果是否准备好。比如,程序自己负责定期检查数据是否从网络读取完毕。
5.2异步 I/O:
- I/O 操作完成后,被调用方-即系统会自动通知调用方,无需调用方主动检查。比如,程序发起网络请求后,当服务器响应时,系统自动调用回调函数来处理结果。
六、结合实际 I/O 场景的例子:
6.1同步阻塞 I/O:
- 例子:假设你正在编写一个读取文件的程序。当你调用
InputStream.read()
方法时,程序会“停下来”等待数据从文件中读取完毕,读取过程中程序不会做其他事情。这类似于你在店里干等着奶茶做好。 - 场景:这是最常见的 I/O 模型,大多数 I/O 操作默认都是阻塞的。虽然简单易用,但在高并发场景下效率较低。
6.2同步非阻塞 I/O:
- 例子:你编写的网络程序调用
Socket
读取数据时,程序不会被阻塞,而是定期检查是否有数据到达(例如使用select()
或poll()
方法)。这就像你一边等奶茶一边玩手机,偶尔看看奶茶是否做好了。 - 场景:在需要处理大量连接的服务器上,使用非阻塞 I/O 可以让程序在等待数据到达时处理其他连接,提高效率。
6.3异步非阻塞 I/O:
- 例子:你编写的程序发起网络请求后,不会等待响应,而是继续处理其他任务。当服务器响应时,系统会自动调用预先定义的回调函数处理数据,就像你去逛街,奶茶做好后通过微信小程序通知你回来取奶茶。
- 场景:这种模式非常适合高性能和高并发场景,例如大型 Web 服务器或需要处理大量 I/O 操作的应用程序。
6.4异步阻塞 I/O:
- 例子:这种组合很少见,但可以假设一种极端情况:你发起了一个异步操作,操作完成后系统会通知你,但是你必须等到收到通知后才能继续进行下一步操作。虽然系统通知了你,但你的程序在等待这个通知时被阻塞住了。就像你去逛街,但一直在等待一条短信通知,而你只能干等着直到收到短信。
- 场景:实际开发中很少遇到这种模式,因为异步的目的是为了避免阻塞。
七、总结
- 阻塞与非阻塞:描述的是调用方在等待 I/O 操作完成时是否能够继续执行其他任务。阻塞意味着调用方必须等待操作完成,而非阻塞意味着调用方可以在等待时执行其他任务。
- 同步与异步:描述的是被调用方在操作完成后是否主动通知调用方。同步意味着调用方需要自己去检查操作是否完成,而异步意味着被调用方会主动通知操作完成。
通过这种思路,我们可以更好地理解 I/O 流中的这些概念,以及如何在不同的场景中选择合适的 I/O 模型。