同步异步阻塞非阻塞
同步和异步关注的是调用方是否需要主动等待调用结果的返回
同步:在前一个任务执行完之后才能进行下一个任务 ;调用方需要等待调用结果的返回
异步:不同的任务之间并不会相互等待;调用方不需要主动等待调用结果的返回,可以采用状态通知、回调函数等方式获取调用结果
同步与异步的区别:请求发出后,是否需要等待结果,才能继续执行其他操作。
阻塞和非阻塞关注的是调用结果返回之前,当前线程是否会被挂起
阻塞:调用结果返回之前,当前线程会被挂起,不会做其他的事情。
非阻塞:调用结果返回之前,当前的线程不会被挂起,可以做其他的事情。
组合出四种模式:同步阻塞、同步非阻塞、异步阻塞、异步非阻塞
1. 同步阻塞: 假设你有一个程序,需要从网络上下载一个文件。在同步阻塞的情况下,当你发出下载请求时,程序会一直等待直到整个文件下载完成,期间不能执行其他任务。类似于你请求下载文件后,不进行其他操作,直到文件完全下载。
2. 同步非阻塞: 继续以上的下载文件的例子,但这次使用同步非阻塞的方式。你发出下载请求后,程序不会一直等待下载完成,而是会反复检查下载状态。它会定期询问下载是否完成,如果没有完成,则继续进行其他操作。这种情况下,程序会周期性地查看下载是否完成,而不是一直停留在下载过程中。
3. 异步阻塞: 在这个例子中,你委托了一个任务,但是你自己却被阻塞,不能做其他事情。例如,你请求某个函数处理一个耗时很长的任务,虽然这个任务是异步执行的,但是你必须一直等待直到它完成才能进行下一步操作。
4. 异步非阻塞: 最后,使用异步非阻塞方式,在你委托了一个耗时的任务后,你不必等待它完成。相反,你可以继续做其他事情。当任务完成时,系统会通知你或者执行预先设置的回调函数,这样你就可以处理任务完成的结果,而不必一直等待。
同步阻塞可以类比为排队,只有当前一个任务完成后,才能执行下一个任务。
同步非阻塞:在等待一个任务完成的过程中可以执行其他任务,然后定期查看任务的状态或结果,不会一直等待。
异步阻塞就是自己做一件事情然后停下来叫别人做,只有别人做完了自己才能接着做(你发出一个任务,但你自己会暂停等待直到该任务完成)。
异步非阻塞模式就像你委托别人做一件事情,但在等待别人完成的过程中,你不会停下来等待。相反,你可以继续做其他事情。当你委托的任务完成时,系统会通知你或者触发一个回调函数,这样你就可以处理任务完成的结果,而不必一直等待。
同步和阻塞的区别:
同步 是指操作之间按照顺序执行,保证了操作的顺序性和同步性,但并不一定导致阻塞。即使是同步操作,如果每个操作执行的时间很短,就不会导致阻塞,因为它们是按照顺序依次执行的。
阻塞 则是指一个操作的执行会等待另一个操作的完成,直到完成后才能继续执行下一个操作。阻塞可以发生在同步操作中,但也可以发生在异步操作中。在同步操作中,阻塞通常是因为操作的依赖性,而在异步操作中,阻塞可能是因为等待外部事件的完成或资源的可用性等原因。
套接字
套接字:网络通信的接口,提供了一种标准的通信方式
Channel
Channel:既能用于读也能用于写。全双工。
在计算机编程中,Channel(通道)是一个用于传输数据的抽象概念,它可以用于不同的 I/O 操作,比如文件读写、网络通信等。通道提供了数据的流动路径,允许数据在源和目的地之间进行传输。
例子:
想象你在厨房准备晚餐。你把食材切好放在砧板上(数据在缓冲区),然后用刀(通道)把这些食材移到锅里(目的地)。在这里,砧板相当于缓冲区,刀则相当于通道,而锅则是数据的最终目的地。
SocketChannel和ServerSocketChannel
SocketChannel
:能通过TCP读写网络中的数据。它代表一个可以进行读取和写入的套接字通道,能够连接到远程服务器的套接字并进行数据传输。通过 SocketChannel
,你可以发起连接、读取数据、写入数据,实现与远程主机的通信。
ServerSocketChannel
:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。它可以监听传入的 TCP 连接请求,并在接收到请求时创建对应的 SocketChannel
以便进行通信。ServerSocketChannel
接受客户端连接请求,并将其委托给 SocketChannel
以建立通信。
区别:
SocketChannel
用于客户端与服务器之间的通信,而ServerSocketChannel
用于服务器接受客户端连接请求
举例:
-
ServerSocketChannel
就像是邮局:邮局(ServerSocketChannel)接收来自不同人的信件(连接请求)。当有人要寄信时,他们将信件(连接请求)送到邮局。邮局工作人员会负责接收并处理这些信件。 -
SocketChannel
就像是信件:一旦邮局(ServerSocketChannel)接收到信件(连接请求),它会创建一封信(SocketChannel)并把它送到收件人(Client)那里。这封信(SocketChannel)可以用来传递信息,比如写信(发送数据)给别人,或者读信(接收数据)来自别人的信息。
Selector
Selector提供了一种高效的方式来管理多个通道的 I/O 操作。其主要作用是允许一个单独的线程监视多个通道的状态,以确定哪些通道已经准备好进行读取、写入或者接受连接。
Selector功能
-
管理多个通道:
Selector
允许单个线程监视多个通道的状态。 -
非阻塞 I/O:通过
Selector
实现非阻塞的 I/O 操作,单个线程可以处理多个通道的 I/O 事件。 -
高效的事件通知机制:
Selector
提供了高效的机制,当一个或多个通道准备好进行 I/O 操作时,通知程序处理这些事件。 -
注册和取消注册:通道可以注册到
Selector
上,指定感兴趣的事件类型(比如读、写等),并且可以取消注册以停止对该通道的监视。 -
节省系统资源:通过单个线程管理多个通道,减少了线程数目,节省了系统资源。
Selector和Channel的区别
-
Channel
是具体的 I/O 通道,负责数据的读取和写入。 -
Selector
是用于管理多个Channel
的工具,允许一个线程有效地监视多个通道,等待它们就绪并处理对应的 I/O 事件。
使用Selector和不使用Selector的区别
不使用Selector
-
资源浪费: 多线程管理通道可能导致资源浪费和管理复杂性增加。每个通道对应一个线程,线程数增加可能导致系统资源消耗增加。
-
低效率: 手动轮询或多线程管理可能会降低系统的效率。手动轮询消耗大量 CPU 资源,而多线程的上下文切换可能会增加系统负担。
使用Selector
-
高效的多路复用:
Selector
允许一个线程同时监视多个通道的状态。它通过单个线程有效地管理多个通道,只有在通道准备好进行 I/O 操作时才会进行处理,提高了系统的效率和性能。 -
资源节省: 相较于为每个通道分配一个线程,使用
Selector
可以减少线程数目,节省系统资源。单个线程管理多个通道的方式更加高效。 -
非阻塞 I/O: 结合
Selector
和非阻塞 I/O,能够实现并发处理多个通道的 I/O 操作,不会因为某个通道的阻塞而影响其他通道的处理。 -
简化管理: 通过
Selector
,可以更容易地管理和监视多个通道的状态变化。注册和取消注册通道都变得简单,并且能够更方便地处理通道的就绪事件。 -
适应大规模并发: 在需要处理大量并发连接或通道的情况下,使用
Selector
能够更好地适应高并发环境,提高系统的吞吐量和响应性能。
BIO NIO AIO的区别
-
AIO:代表"Asynchronous I/O",它是一种编程模型,允许程序进行异步的输入输出操作。它能够让程序在等待输入输出操作完成时继续执行其他任务,而不是被阻塞等待。一个常见的例子是Python的
asyncio
库,它允许开发者编写异步的、并发的代码,例如在网络编程中处理多个客户端请求而无需阻塞其他请求。 -
NIO:代表"Non-blocking I/O",与AIO类似,它也是一种允许程序进行非阻塞式输入输出操作的编程模型。Java的NIO(New I/O)提供了一种更高效的I/O操作方式,它使用了通道(Channel)和缓冲区(Buffer)来进行数据传输,允许在一个线程中处理多个通道。
-
BIO:代表"Blocking I/O",与AIO和NIO相反,它是一种阻塞式的输入输出模型。在进行输入输出操作时,程序会被阻塞直到操作完成。Java的传统I/O操作就是典型的BIO,每个I/O操作都会阻塞当前线程。
举个例子来说,假设你要从一个远程服务器上获取数据:
-
使用BIO,你的程序会发送一个请求并等待服务器响应,期间程序会被阻塞,无法执行其他任务。
-
使用NIO,你可以在一个线程中管理多个连接,当一个连接在等待数据时,程序可以继续处理其他连接,不会阻塞整个线程。
-
使用AIO,你可以发起多个请求,在等待数据返回的时候不会阻塞程序执行,允许处理其他任务,当数据准备好后会通过回调函数进行处理。
NIO:在使用NIO时,一个线程可以管理多个通道(比如网络连接)。当一个通道在等待数据时,你可以切换去检查其他通道,而不是等待当前通道数据准备好。这是因为NIO使用了一种事件驱动的机制,称为选择器(Selector)。 想象你是一位电话接线员(线程),有很多电话线(通道)连接到你的交换台。这些电话线都可以接收和发送信息。当你接听一个电话,如果对方在思考该说什么,你并不会一直等待,而是去接听其他来电,或者处理其他事情。你会周期性地检查每条线路,看看是否有人在通话,如果有人在说话,你就处理他们的请求。 在编程中,Selector就像这位接线员,它帮助程序监控多个通道的状态。当你的程序使用Selector时,它可以等待多个通道中的任何一个变得可用,而不会阻塞整个线程。这意味着程序可以同时管理多个通道,只在有数据可处理时才会去处理它们,而不是等待每一个通道的数据准备好。
AIO(Asynchronous I/O)是一种编程模型,类似于前面提到的NIO,但在处理I/O操作时更为高级,它允许程序在发起I/O操作后不必等待其完成,而是可以继续执行其他任务。 举个例子,假设你在使用AIO来处理文件读取操作。你可以发起多个文件读取请求,然后继续执行其他任务,而不必等待这些文件读取操作完成。一旦某个文件读取完成,系统会通过回调函数或者事件来通知你数据已经准备好了,然后你可以处理这些数据。
AIO和NIO的区别
-
设计理念:
-
NIO:主要关注于非阻塞式I/O。它使用通道(Channel)和缓冲区(Buffer)来实现非阻塞式读写操作,通过Selector来监控多个通道的状态,使得一个线程可以管理多个通道,并在一个通道等待数据时切换处理其他通道。
-
AIO:着重于异步I/O。AIO允许在发起I/O操作后,程序可以继续执行其他任务,当I/O操作完成时会通过回调函数或事件通知程序,使得程序不必等待I/O操作完成就可以进行其他工作。
-
-
使用场景:
-
NIO:适用于需要处理大量连接但每个连接读写操作不会花费太多时间的场景,比如网络编程中处理多个客户端请求。
-
AIO:更适合处理每个I/O操作耗时较长的情况,比如文件操作或网络操作中的大文件传输。AIO适合处理需要在等待I/O完成时执行其他任务的情况。
-
-
支持的语言和平台:
-
NIO:Java中提供了NIO,许多编程语言也有类似的非阻塞I/O机制,如Python的
selectors
模块等。 -
AIO:AIO在一些编程语言和平台上有不同的实现,比如在Java中也有AIO的支持,但并不是所有编程语言和平台都原生支持AIO。
-
例子:
-
NIO(Non-blocking I/O):
-
想象你在一个餐厅中,有多张桌子代表多个客户端连接。每个桌子(客户端连接)上的顾客(数据请求)都想点菜(发送数据)和吃菜(接收数据)。
-
作为服务员,你在所有的桌子之间来回走动,当一个桌子的顾客点完菜(发送数据)后,你把订单记录下来,并告诉厨房(系统)去做菜。然后你转身去服务另一张桌子的顾客,你不会等待厨房做完菜(数据准备好),而是会继续前往下一张桌子记录订单,最后当菜做好(数据准备好)时,你会再回到对应的桌子上将菜上好并通知顾客。
-
这就像是NIO,你能够管理多个桌子(客户端连接),当一个桌子在等待食物(数据)时,你可以前往其他桌子记录订单,不会因为一个桌子在等待而停下来。
-
-
AIO(Asynchronous I/O):
-
在这个例子中,AIO就像是你在餐厅里有一个菜单上列出了所有顾客的点单,你可以看到哪些顾客点了什么菜。
-
你不需要一直等待某个桌子的菜(数据)做好,而是看着菜单,当某桌的菜做好了,厨房会通知你(回调函数或事件通知),然后你再去给对应的桌子上菜。
-
这就像AIO,你发起了多个请求(记录了所有点单),当菜(数据)准备好时,系统会通过回调或事件告诉你,然后你去处理这些准备好的菜(数据)。
-
在NIO中,你会主动监控和处理每个I/O操作的状态,而在AIO中,你更像是被动地等待事件的发生,然后再处理