Java IO模型

参考文章
https://zhuanlan.zhihu.com/p/115912936
https://blog.csdn.net/wteruiycbqqvwt/article/details/90299610
https://time.geekbang.org/column/article/100307

I/O 就是计算机内存与外部设备之间拷贝数据的过程。我们知道 CPU 访问内存的速度远远高于外部设备,因此 CPU 是先把外部设备的数据读到内存里,然后再进行处理。

I/O 模型是为了解决内存和外部设备速度差异的问题.

阻塞或非阻塞是指应用程序在发起 I/O 操作时,是立即返回还是等待。

同步和异步,是指应用程序在与内核通信时,数据从内核空间到应用空间的拷贝,是由内核主动发起还是由应用程序来触发。

Java I/O模型

Java I/O 模型对于一个网络 I/O 通信过程,比如网络数据读取,会涉及两个对象,一个是调用这个 I/O 操作的用户线程,另外一个就是操作系统内核。一个进程的地址空间分为用户空间和内核空间,用户线程不能直接访问内核空间。
当用户线程发起 I/O 操作后,网络数据读取操作会经历两个步骤:

  • 用户线程等待内核将数据从网卡拷贝到内核空间。
  • 内核将数据从内核空间拷贝到用户空间。

I/O 多路复用:用户线程的读取操作分成两步了,线程先发起 select 调用,目的是问内核数据准备好了吗?等内核把数据准备好了,用户线程再发起 read 调用。在等待数据从内核空间拷贝到用户空间这段时间里,线程还是阻塞的。那为什么叫 I/O 多路复用呢?因为一次 select 调用可以向内核查多个数据通道(Channel)的状态,所以叫多路复用。
在这里插入图片描述

BIO的阻塞

之前看BIO和NIO的时候,一直以为BIO的阻塞同时只有一个请求可以访问,其实并不是这样的。

  • 这里的是否阻塞是指,用户线程在询问内核有没有准备好数据是立刻返回,还是直到数据准备完再返回
  • 因此,当有A用户一个请求访问服务器的时候,在数据全部到达内核态的前,程序的业务线程在询问内核数据是否准备好时是阻塞的。
    与此同时B用户一个请求访问服务器,程序会开启一个线程(一般是从线程池里去)去询问B用户请求的数据是否有准备好。并不会因为A用户请求的数据没有全部到达,而不读取B用户请求的数据。甚至可以是A用户的数据没有准备好,B用户的请求已经处理完成。
  • 也就是说服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理
  • 只有一个请求可以访问,这种情况是,只有一个业务线程类处理请求,B用户的请求要等待A用户的请求完成。然而实际上,收到的请求,是由业务线程池里的线程取出来执行,因此基本不会出现只有一个请求可以访问的情况。
  • 造成这种认识的原因是把阻塞和串行的概念混淆了,阻塞不代表是串行,一个线程阻塞在访问内核是否准备好数据,并不妨碍其他线程也去访问内核是否准备好数据。
  • 总结:BIO的阻塞并不是说只有一个线程去内核询问内核有没有准备好数据并拷贝数据,而是说在询问内核数据有没有准备好的时候是阻塞,但可以由多个线程去内核询问不同的连接数据是否准备好,等待内核数据准备好并拷贝回用户态。

那NIO是什么呢?
NIO跟BIO的区别是当有请求过来的时候,NIO的程序不是对每个请求都用业务线程池里的线程去内核询问数据有没有准备好,而是将这个请求注册到select上,而是程序会在后台开启一个线程(这个线程并不是业务线程池里的线程),去询问这些通道数据有没有准备好,如果准备好,再让业务类线程去取数据。

BIO的连接释放时,是否线程是回收,还是怎么样?
线程是业务线程池里的线程,所以被回收了。

select epoll poll

linux系统把网络请求以一个fd来标识.
应用线程通过调用select函数(IO复用模型的思路就是系统提供了一种函数可以同时监控多个fd的操作,这个函数就是我们常说到的select、poll、epoll函数)就可以同时监控多个fd,select函数监控的fd中只要有任何一个数据状态准备就绪了,select函数就会返回可读状态,这时询问线程再去通知处理数据的线程,对应线程此时再发起recvfrom请求去读取数据。

select、poll、epoll 区别总结:
1、支持一个进程所能打开的最大连接数

select:单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。

poll:poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的。

epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接。

2、FD剧增后带来的IO效率问题

select:因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。

poll:同上

epoll:因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。

3、 消息传递方式

select:内核需要将消息传递到用户空间,都需要内核拷贝动作

poll:同上

epoll:epoll通过内核和用户空间共享一块内存来实现的。

总结:

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。

1、表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。

2、select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值