详解为什么IO多路复用能提高效率

一. 什么是网络IO?

网络IO,拿read操作举例,就是程序(内存)从网卡读数据的过程。网卡相当于是文件IO的磁盘。客户端发过来的内容会先存在网卡中。

2.  使用缓冲字节流读写效率为什么比普通字节流读写效率好?(这个原理会类比到下面讲的IO多路复用中的Buffer)

因为普通字节流执行read操作时,直接将数据拷贝给int b。 而缓冲字节流会在内存中设立一块缓冲区,长度为8192的字节数组。此时在执行read操作时,磁盘会将数据一次性地读8192个字节到缓冲区,然后int b再一个一个地去这个缓冲区去拷贝。当int b把缓冲区读完后,缓冲区再区磁盘中拷贝8192个字节。直到读完。

这样做的好处是,磁盘与内存之间的数据拷贝会非常耗时且消耗cpu,而内存内部自己的拷贝则很快。所以设立缓冲区会减少磁盘与内存之间的拷贝次数,提高效率。

write操作也一样,int b先写到write缓冲区,写满缓冲区后,缓冲区一次性拷贝到磁盘中。

3. IO多路复用为什么和多线程一样工作效率高?

1.并发是单个cpu来处理多个线程,比如说下图中的红线代表cpu,蓝线代表的是线程。并发是红线在两个蓝线之间来回切换。 而并行是有两个cpu同时处理两个线程。

所以一个cpu,但是开启两个线程,如果这两个线程在执行过程中没有任何的等待时间,那么开启多线程是没有意义的,并不能提高执行时间。但是如果其中有一个线程执行过程出现了阻塞等待,那么此时cpu就去处理另一个线程,而不是只等着当前线程,所以会提高执行效率。

所以开启多线程他不是并行而是并发!!!!!!!!!!!!!

交替进行:

那么开启多线程之后也就是可以让cpu没有等待的时间,不停的在线程之间切换去执行。

而IO多路复用虽然不是多线程,但是会让一个单线程去管理每一个socket。就相当于让一个单线程去管理一些多线程(因为一个socket就是一个线程),哪个socket线程就绪就让cpu执行哪个线程。这也相当于没有了等待时间。这就是为什么IO多路复用时单线程但是和多线程效率一样的原因。

4. IO多路复用的原理

NIO 主要包括以下三个核心组件:

  • Buffer(缓冲区):NIO 读写数据都是通过缓冲区进行操作的。读操作的时候将 Channel 中的数据填充到 Buffer 中,而写操作时将 Buffer 中的数据写入到 Channel 中。
  • Channel(通道):Channel 是一个双向的、可读可写的数据传输通道,NIO 通过 Channel 来实现数据的输入输出。通道是一个抽象的概念,它可以代表文件、套接字或者其他数据源之间的连接。
  • Selector(选择器):允许一个线程处理多个 Channel,基于事件驱动的 I/O 多路复用模型。所有的 Channel 都可以注册到 Selector 上,由 Selector 来分配线程来处理事件。

这个Buffer的工作原理就和上面的缓冲字节流的Buffer原理差不多。这个Buffer是内核缓冲区,存在于内核态,存在Buffer中的数据还没有被发送到用户态中,所以程序还使用不了数据,此时线程就回去执行其他的操作。

当数据将缓冲区全部读或者写好后,selector就会去通知线程进行相应的read和write操作,比如read操作,在线程接到通知后,就会启动程序,将Buffer中的数据拷贝到用户空间去。

Selector(选择器)

Selector(选择器) 是 NIO 中的一个关键组件,它允许一个线程处理多个 Channel。Selector 是基于事件驱动的 I/O 多路复用模型,主要运作原理是:通过 Selector 注册通道的事件,Selector 会不断地轮询注册在其上的 Channel。当事件发生时,比如:某个 Channel 上面有新的 TCP 连接接入、读和写事件,这个 Channel 就处于就绪状态,会被 Selector 轮询出来。Selector 会将相关的 Channel 加入到就绪集合中。通过 SelectionKey 可以获取就绪 Channel 的集合,然后对这些就绪的 Channel 进行响应的 I/O 操作。

一个多路复用器 Selector 可以同时轮询多个 Channel,由于 JDK 使用了 epoll() 代替传统的 select 实现,所以它并没有最大连接句柄 1024/2048 的限制。这也就意味着只需要一个线程负责 Selector 的轮询,就可以接入成千上万的客户端。

Selector 可以监听以下四种事件类型:

  1. SelectionKey.OP_ACCEPT:表示通道接受连接的事件,这通常用于 ServerSocketChannel
  2. SelectionKey.OP_CONNECT:表示通道完成连接的事件,这通常用于 SocketChannel
  3. SelectionKey.OP_READ:表示通道准备好进行读取的事件,即有数据可读。
  4. SelectionKey.OP_WRITE:表示通道准备好进行写入的事件,即可以写入数据。

Selector是抽象类,可以通过调用此类的 open() 静态方法来创建 Selector 实例。Selector 可以同时监控多个 SelectableChannelIO 状况,是非阻塞 IO 的核心。

一个 Selector 实例有三个 SelectionKey 集合:

  1. 所有的 SelectionKey 集合:代表了注册在该 Selector 上的 Channel,这个集合可以通过 keys() 方法返回。
  2. 被选择的 SelectionKey 集合:代表了所有可通过 select() 方法获取的、需要进行 IO 处理的 Channel,这个集合可以通过 selectedKeys() 返回。
  3. 被取消的 SelectionKey 集合:代表了所有被取消注册关系的 Channel,在下一次执行 select() 方法时,这些 Channel 对应的 SelectionKey 会被彻底删除,程序通常无须直接访问该集合,也没有暴露访问的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值