Java网络IO涵盖的知识体系很广泛,本文将简单介绍Java网络IO的相关知识
为了保护操作系统的安全,会将内存分为用户空间和内核空间两个部分。如果用户想要操作内核空间的数据,则需要把数据从内核空间拷贝到用户空间。
举个栗子,如果服务器收到了从客户端过来的请求,并且想要进行处理,那么需要经过这几个步骤:
因此我们可以将服务器接收消息理解为两个阶段:
1. 等待数据到达
2.将数据从内核空间拷贝到用户空间
在此以Linux操作系统为例。Linux是一个将所有的外部设备都看作是文件来操作的操作系统,在它看来:everything is a file,那么我们就把对于外部设备的操作都看作是对文件进行操作。而且我们对一个文件进行读写,都需要通过调用内核提供的系统调用。
而在Linux中,一个基本的IO会涉及到两个系统对象:一个是调用这个IO的进程对象(用户进程),另一个是系统内核。也就是说,当一个read操作发生时,将会经历这些阶段:
在此期间会发生几种IO操作:
同步IO:当用户发出IO请求操作后,内核会去查看要读取的数据是否就绪,如果没有,就一直等待。期间用户线程或内存会不断地轮询数据是否就绪。当数据就绪时,再把数据从内核拷贝到用户空间。
异步IO:用户线程只需发出IO请求和接收IO操作完成通知,期间的IO操作由内核自动完成,并发送通知告知用户线程IO操作已经完成。也就是说,在异步IO中,并不会对用户线程产生任何阻塞。
阻塞IO:当用户线程发起一个IO请求操作,而内核要操作的数据还没就绪,则当前线程被挂起,阻塞等待结果返回。
非阻塞IO:如果数据没有就绪,就会返回一个标志信息告知用户线程,当前的数据还没有就绪。当前线程在获得此次请求结果的过程中,还可以做点其他事情。
可能会有读者觉得,怎么同步IO、异步IO和阻塞IO、非阻塞IO的操作好相似,为什么要它们都分出来呢?笔者认为,这同步、异步和阻塞、非阻塞是从不同角度来看待问题的。
同步就是当一个任务A的完成需要依赖另一个任务B时,只有等到B任务完成后,A才能成功地进行,这是一种可靠的任务队列。要么都成功,要么都失败,两个任务的状态可以保持一致。
异步是不需要等待任务B完成,只是通知任务B要完成什么工作,任务A也立即执行,只要任务A自己执行完了那么整个任务就算完成了。至于任务B最终是否真正完成,A任务无法确定,所以这是不可靠的一种任务队列。
举个栗子,假如小J要去银行柜台办事,拿号排队。如果他只盯着号码提示牌,还时不时问是否到他了,这就是同步;如果他拿了号之后就去打电话了,等到排到他的时候柜员通知他去办理业务,这就是异步。他们之间的区别就在于,等待消息通知的方式不同。
阻塞就是指在调用结果返回之前,当前线程会被挂起,一直处于等待消息通知的状态,不能执行其他业务。只有当调用结果返回之后才能进行其他操作。
非阻塞与阻塞的概念相对应,就是指不能立即得到结果之前,该函数不会阻塞当前线程,而是会立即返回。虽然非阻塞的方式看上去可以明显提高CPU的利用率,但是也会使系统的线程切换增加,需要好好评估增加的CPU执行时间能不能步长系统的切换成本。
我们继续用上面的栗子,小J无论是在排队还是拿号等通知,如果在这个等待的过程中,小J除了等待消息通知之外就做不了其他的事情,那么该机制就是阻塞的。如果他可以一边打电话一边等待,这个状态就是非阻塞的。
同步、异步与阻塞、非阻塞
其实可能会有其他读者把同步与阻塞等同起来,实际上这两个是不同的。对于同步来说,很多时候当前线程还是在激活状态,只是逻辑上当前函数没有返回而已,此时,线程也会去处理其他的消息。也就是说,同步、阻塞其实是在消息通知机制下从不同角度对当前线程状态的描述。
在这里,同步与阻塞体现在:
同步:小J等待队伍排到他办理业务;
阻塞:小J在等待队伍排到他的过程中,不做其他任务处理。
5.2 异步阻塞形式
如果小J在银行等待办理业务的时候,领了号,这时候就采用了异步的方式去等待消息被触发(通知),等着柜员喊他的号而不是时刻盯着是不是排到他了。但是在这段时间里,他还是不能离开银行去做其他的事情,那么很显然,他被阻塞在这个等待喊号的操作上了。
在这里,异步与阻塞体现在:
异步:排到小J的话柜员会喊他的号码;
阻塞:等待喊号的过程中,不能做其他事情。
5.3 同步非阻塞形式
实际上效率也是低下。小J在排队的过程中可以打电话,但是要边打电话边看看还有多久才排到他。如果将打电话和观察排队情况看成是程序中的两个操作的话,这个程序需要在这两个不同的行为之间来回切换。
在这里,同步与非阻塞体现在:
同步:排队等待轮到他办理业务;
非阻塞:可以在排队的过程中打电话,只不过要时不时看看还要多久才排到他办理业务。
5.4 异步非阻塞形式
这是一个效率更高的模式。小J在拿号之后可以去打电话,只要等待柜员喊号就可以了,在这里打电话是等待着的事情,而通知小J办理业务是柜员的事情。
在这里,异步和非阻塞体现在:
异步:柜员喊小J去办理业务;
非阻塞:在等待喊号的过程中,小J去打电话,只要接收到柜员喊号的通知即可,无需关注是否队伍的进度。
也就是说,同步和异步仅需关注消息如何通知的机制,而阻塞和非阻塞关注的是在等待消息通知的过程中能不能去做别的事。在同步情况下,是由处理者自己去等待消息是否被触发,而异步情况下是由触发机制来通知处理者处理业务。