Java网络IO涵盖的知识体系很广泛,本文将简单介绍

Java网络IO覆盖的知识体系非常广泛。本文将简单介绍Java网络IO的相关知识。

从操作系统开始

为了保护操作系统的安全,内存分为用户空间和内核空间两部分。如果用户想要操作内核空间中的数据,则需要将数据从内核空间复制到用户空间。

例如,如果服务器收到来自客户端的请求并想要处理它,它需要经过以下步骤:

因此,我们可以将服务器接收消息理解为两个阶段:

操作系统中的IO

这里我们以Linux操作系统为例。Linux是一个把所有外部设备都当作文件来操作的操作系统。在它看来:一切都是文件,那么我们把对外部设备的操作看作是对文件的操作。而我们需要调用内核提供的系统调用来读写文件。

在Linux中,一个基本的IO涉及到两个系统对象:一个是调用这个IO的进程对象(用户进程),另一个是系统内核。也就是说,当一个读操作发生时,它会经历这些阶段:

在此期间会发生几个 IO 操作:

有的读者可能会认为同步IO和异步IO的操作类似于阻塞IO和非阻塞IO。他们为什么要分开?作者认为同步、异步和阻塞,非阻塞是从不同的角度来看问题。

同步和异步

同步和异步主要是从消息通知的角度。

同步是指当一个任务A的完成需要依赖另一个任务B时,A只有在B任务完成后才能成功执行,是一个可靠的任务队列。要么都成功,要么都失败,两个任务的状态可以保持不变。

异步意味着不需要等待任务B完成,而是通知任务B完成什么工作,任务A会立即执行。只要任务A完成了自己的执行,整个任务就完成了。至于最终任务B是否真正完成,任务A无法确定,所以这是一种不可靠的任务队列。

比如小J想去银行柜台办事,拿号排队。如果他只是盯着数字标志,不时问他是否在计算机中io设备是什么,这是同步的;如果他拿了号码然后打电话,当柜员到达他的线路时,柜员通知他去办理业务,这是异步的。它们的区别在于等待消息通知的方式不同。

阻塞和非阻塞

阻塞和非阻塞主要是从等待消息通知时的状态来看。

阻塞是指在调用结果返回之前,当前线程会被挂起,等待消息通知,不能执行其他服务。其他操作只有在调用结果返回后才能进行。

非阻塞对应阻塞的概念,意思是函数不会阻塞当前线程,直到不能立即得到结果,而是立即返回。非阻塞方式虽然看似显着提高了CPU利用率,但也会增加系统的线程切换。有必要评估增加的 CPU 执行时间是否会增加系统的切换成本。

我们继续使用上面的栗子。无论小J是在排队还是取号等待通知,如果在这个等待过程中,小J除了等待消息通知之外什么都做不了,那么这个机制就被阻塞了。如果他可以在调用时等待,则状态为非阻塞。

同步、异步和阻塞、非阻塞

事实上,可能还有其他读者将同步等同于阻塞,但实际上两者是不同的。对于同步,很多时候当前线程仍然处于活动状态,但逻辑上当前函数还没有返回。这时候,线程也会处理其他消息。也就是说,同步和阻塞其实是在消息通知机制下,从不同角度对当前线程状态的描述。

5.1 同步阻塞形式

这是效率最低的方法。以上面的栗子为例,就是小J心不在焉地排队,什么都不做。

在这里,同步和阻塞体现在:

5.2 异步阻塞形式

如果小J在银行等待交易时收到一个号码,他使用异步方法等待消息被触发(通知),等待柜员拨打他的号码,而不是一直盯着他是否在队列。. 但是这段时间,他还是不能离开银行去做其他事情,所以很明显,他在等待电话的这个操作中被阻塞了。

在这里,异步和阻塞体现在:

5.3 同步非阻塞形式

事实上,效率也很低。小J在排队的时候可以打电话,但是他得打个电话看看要多久才能到。如果您将拨打电话和观看队列视为程序中的两个操作,那么程序需要在这两种不同的行为之间来回切换。

在这里,同步和非阻塞体现在:

5.4 异步非阻塞形式

这是一种更有效的模式。拿到号码后,小J可以拨打电话,只要等待柜员拨打号码即可。在这里,打个电话是等待的事情,通知小J办理业务是柜员的事情。

在这里,异步和非阻塞体现在:

也就是说,同步和异步只需要关注消息如何通知的机制,而阻塞和非阻塞则关注在等待消息通知的同时是否可以做其他事情。同步情况下,handler等待消息被触发,而异步情况下,触发机制通知handler处理业务。

Linux的五种IO模型

在了解了Linux操作系统的IO操作,以及同步与异步、阻塞与非阻塞的概念之后,我们再来看看Linux系统中实现的基于同步、异步、阻塞、非阻塞的五种IO模型-阻塞。以Linux下的系统调用recv为例,它用于接收来自socket的消息。因为是系统调用,所以调用的时候会在运行一段时间后从用户空间切换到内核空间,然后再切换回来。. 默认情况下,recv 等待网络数据到达并复制到用户空间或在发生错误时返回。

6.1 同步阻塞IO模型

从系统调用 recv 到数据从内核复制到用户空间再返回期间,该进程始终处于阻塞状态。相当于小J想去柜台办理业务。如果柜台业务繁忙,他必须排队,直到他完成业务才能做其他事情。显然,这种 IO 模型是同步和阻塞的。

6.2 同步非阻塞IO模型

无论是否获取数据,这里 recv 都会返回。如果没有数据,则在一段时间后调用 recv 来查看,依此类推。就像小J来到柜台办理业务,发现柜员在休息,他就走了,过一会回来看看有没有营业,直到最后柜员开门了,才办理了业务. 而当小J中途离开时计算机中io设备是什么,他可以做自己的事情。然而,这个模型只有在没有数据要检查的时候才是非阻塞的。当数据到达时,仍然要等待数据复制到用户空间(用于业务处理),所以还是同步IO。

6.3 IO复用模型

在 IO 多路复用模型中,会在调用 recv 之前调用 select 或 poll。这两个系统调用都可以在内核准备好数据(网络数据已到达内核)时通知用户进程。调用recv时必须有数据。所以在这个模型中,进程阻塞在 select 或 poll 上,而不是在 recv 上。相当于小J来银行办理业务。大堂经理告诉他,现在各个柜台都有人在处理业务,有空位他会告诉他的。于是小 J 等了又等(正在调用 select 或 poll)。过了一会,大堂经理告诉他有一个空置的柜台可以办理业务,但是具体的柜台号码,你自己找,所以小J只能看每个柜台。

6.4 信号驱动IO模型

在这里,将通过调用 sigaction 注册信号函数。当内核数据准备好后,系统会中断当前程序并执行信号函数(这里调用recv)。相当于小J让大堂经理在柜台有空位时通知他(签到信号功能)。过了一会,大堂经理通知他,因为他是银行的VIPPP会员,所以特地开了一个柜台给他办理业务。小J去特座柜台做生意。但是即使在等待的过程中是非阻塞的,在处理业务的过程中仍然是同步的。

6.5 异步IO模型

调用 aio_read 使内核准备好数据,并在将数据复制到用户进程空间后执行预先指定的函数。就好像,小J让大堂经理在业务完成后去查看业务,过程中小J可以做自己的事情。这是真正的异步 IO。

我们可以看到前四个模型都是同步IO,因为将内核数据复制到用户空间的过程被阻塞了。最后一种异步IO,通过将IO操作交给操作系统,当前进程不关心具体IO的实现,然后通过回调函数或者信号量通知当前进程直接处理IO返回结果。

BIO、NIO、AIO的区别

上面提到了IO的四种模式:同步阻塞IO、同步非阻塞IO、异步阻塞IO、异步非阻塞IO,JavaIO中提供了三种模式的实现:BIO(同步阻塞IO)、NIO(同步非阻塞IO)阻塞IO),AIO(异步非阻塞IO)。至于这四种模式的区别,上面已经详细介绍过了。接下来,笔者将介绍这三种JavaIO类型的区别。

结语

本文从文件读写的操作系统入手,介绍了同步、异步、阻塞、非阻塞以及它们的组合IO模式。它还理解了Linux操作系统中的五种IO模型,并返回到JavaIO。BIO、NIO、AIO的区别。

如果这篇文章对你有帮助,请给个赞,这将是我最大的动力~

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论