BIO
有了Block的定义,就可以讨论BIO和NIO了。BIO是Blocking IO的意思。在类似于网络中进行read, write, connect一类的系统调用时会被卡住。
举个例子,当用read去读取网络的数据时,是无法预知对方是否已经发送数据的。因此在收到数据之前,能做的只有等待,直到对方把数据发过来,或者等到网络超时。
对于单线程的网络服务,这样做就会有卡死的问题。因为当等待时,整个线程会被挂起,无法执行,也无法做其他的工作。
顺便说一句,这种Block是不会影响同时运行的其他程序(进程)的,因为现代操作系统都是多任务的,任务之间的切换是抢占式的。这里Block只是指Block当前的进程。
于是,网络服务为了同时响应多个并发的网络请求,必须实现为多线程的。每个线程处理一个网络请求。线程数随着并发连接数线性增长。这的确能奏效。实际上2000年之前很多网络服务器就是这么实现的。但这带来两个问题:
- 线程越多,Context Switch就越多,而Context Switch是一个比较重的操作,会无谓浪费大量的CPU。
- 每个线程会占用一定的内存作为线程的栈。比如有1000个线程同时运行,每个占用1MB内存,就占用了1个G的内存。
也许现在看来1GB内存不算什么,现在服务器上百G内存的配置现在司空见惯了。但是倒退20年,1G内存是很金贵的。并且,尽管现在通过使用大内存,可以轻易实现并发1万甚至10万的连接。但是水涨船高,如果是要单机撑1千万的连接呢?
问题的关键在于,当调用read接受网络请求时,有数据到了就用,没数据到时,实际上是可以干别的。使用大量线程,仅仅是因为Block发生,没有其他办法。
当然你可能会说,是不是可以弄个线程池呢?这样既能并发的处理请求,又不会产生大量线程。但这样会限制最大并发的连接数。比如你弄4个线程,那么最大4个线程都Block了就没法响应更多请求了。
要是操作IO接口时,操作系统能够总是直接告诉有没有数据,而不是Block去等就好了。于是,NIO登场。

BIO在处理网络请求时可能导致线程阻塞,当并发连接数增加,需要创建大量线程,消耗资源。NIO的出现解决了这个问题,它提供非阻塞I/O,允许在等待数据时执行其他任务,减少了线程使用,提升了系统效率。
https://www.bilibili.com/video/BV1qL411u7eE?spm_id_from=333.999.0.0
1169

被折叠的 条评论
为什么被折叠?



