IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~(io和nio是什么)

  本篇文章为你整理了IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~(io和nio是什么)的详细内容,包含有io,nio,aio io和nio是什么 .bio、nio、aio 有什么区别? io和bio的区别 IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~,希望能帮助你了解 IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~。

  stream 不会自动缓冲数据,channel 会利用系统提供的发送缓冲区、接收缓冲区(更为底层)

  stream 仅支持阻塞 API,channel 同时支持阻塞、非阻塞 API,网络 channel 可配合 selector 实现多路复用

  二者均为全双工,即读写可以同时进行

  虽然 Stream 是单向流动的,但是它也是全双工的

  2、IO 模型

  同步:线程自己去获取结果(一个线程)

  例如:线程调用一个方法后,需要等待方法返回结果

  
当调用一次 channel.read 或 stream.read 后,会由用户态切换至操作系统内核态来完成真正数据读取,而读取又分为两个阶段,分别为:

  等待数据阶段

  复制数据阶段

  
 

  根据 UNIX 网络编程 - 卷 I,IO 模型主要有以下几种

  阻塞 IO

  用户线程进行 read 操作时,需要等待操作系统执行实际的 read 操作,此期间用户线程是被阻塞的,无法执行其他操作

  非阻塞IO

  
用户线程

  在一个循环中一直调用 read 方法,若内核空间中还没有数据可读,立即返回

  只是在等待阶段非阻塞

  
 

  Java 中通过 Selector 实现多路复用

  当没有事件是,调用 select 方法会被阻塞住

  一旦有一个或多个事件发生后,就会处理对应的事件,从而实现多路复用

  多路复用与阻塞IO的区别

  阻塞 IO 模式下,若线程因 accept 事件被阻塞,发生 read 事件后,仍需等待 accept 事件执行完成后,才能去处理 read 事件

  多路复用模式下,一个事件发生后,若另一个事件处于阻塞状态,不会影响该事件的执行

  线程 1 调用方法后理解返回,不会被阻塞也不需要立即获取结果

  当方法的运行结果出来以后,由线程 2 将结果返回给线程 1

  3、零拷贝

  零拷贝指的是数据无需拷贝到 JVM 内存中,同时具有以下三个优点

  更少的用户态与内核态的切换

  不利用 cpu 计算,减少 cpu 缓存伪共享

  零拷贝适合小文件传输

  传统 IO 问题

  传统的 IO 将一个文件通过 socket 写出

  

File f = new File("helloword/data.txt");

 

  RandomAccessFile file = new RandomAccessFile(file, "r");

  byte[] buf = new byte[(int)f.length()];

  file.read(buf);

  Socket socket = ...;

  socket.getOutputStream().write(buf);

  

 

  内部工作流如下

  
Java 本身并不具备 IO 读写能力,因此 read 方法调用后,要从 Java 程序的用户态切换至内核态,去调用操作系统(Kernel)的读能力,将数据读入内核缓冲区。这期间用户线程阻塞,操作系统使用 DMA(Direct Memory Access)来实现文件读,其间也不会使用 CPU

  DMA 也可以理解为硬件单元,用来解放 cpu 完成文件 IO

  
从内核态切换回用户态,将数据从内核缓冲区读入用户缓冲区(即 byte [] buf),这期间 CPU 会参与拷贝,无法利用 DMA

  
调用 write 方法,这时将数据从用户缓冲区(byte [] buf)写入 socket 缓冲区,CPU 会参与拷贝

  
接下来要向网卡写数据,这项能力 Java 又不具备,因此又得从用户态切换至内核态,调用操作系统的写能力,使用 DMA 将 socket 缓冲区的数据写入网卡,不会使用 CPU

  
可以看到中间环节较多,java 的 IO 实际不是物理设备级别的读写,而是缓存的复制,底层的真正读写是操作系统来完成的

  用户态与内核态的切换发生了 3 次,这个操作比较重量级

  数据拷贝了共 4 次

  NIO优化

  通过 DirectByteBuf

  ByteBuffer.allocate(10)

  底层对应 HeapByteBuffer,使用的还是 Java 内存

  
 

  大部分步骤与优化前相同,唯有一点:Java 可以使用 DirectByteBuffer 将堆外内存映射到 JVM 内存中来直接访问使用

  这块内存不受 JVM 垃圾回收的影响,因此内存地址固定,有助于 IO 读写

  Java 中的 DirectByteBuf 对象仅维护了此内存的虚引用,内存回收分成两步

  DirectByteBuffer 对象被垃圾回收,将虚引用加入引用队列

  当引用的对象 ByteBuffer 被垃圾回收以后,虚引用对象 Cleaner 就会被放入引用队列中,然后调用 Cleaner 的 clean 方法来释放直接内存

  DirectByteBuffer 的释放底层调用的是 Unsafe 的 freeMemory 方法

  
进一步优化 1

  以下两种方式都是零拷贝,即无需将数据拷贝到用户缓冲区中(JVM 内存中)

  底层采用了 linux 2.1 后提供的 sendFile 方法,Java 中对应着两个 channel 调用 transferTo/transferFrom 方法拷贝数据

  Java 调用 transferTo 方法后,要从 Java 程序的用户态切换至内核态,使用 DMA 将数据读入内核缓冲区,不会使用 CPU

  数据从内核缓冲区传输到 socket 缓冲区,CPU 会参与拷贝

  最后使用 DMA 将 socket 缓冲区的数据写入网卡,不会使用 CPU

  这种方法下

  只发生了 1 次用户态与内核态的切换

  数据拷贝了 3 次

  进一步优化 2

  linux 2.4 对上述方法再次进行了优化

  Java 调用 transferTo 方法后,要从 Java 程序的用户态切换至内核态,使用 DMA 将数据读入内核缓冲区,不会使用 CPU

  只会将一些 offset 和 length 信息拷入 socket 缓冲区,几乎无消耗

  使用 DMA 将 内核缓冲区的数据写入网卡,不会使用 CPU

  整个过程仅只发生了 1 次用户态与内核态的切换,数据拷贝了 2 次

  4、AIO

  AIO 用来解决数据复制阶段的阻塞问题

  同步意味着,在进行读写操作时,线程需要等待结果,还是相当于闲置

  异步意味着,在进行读写操作时,线程不必等待结果,而是将来由操作系统来通过回调方式由另外的线程来获得结果

  
Windows 系统通过 IOCP 实现了真正的异步 IO

  Linux 系统异步 IO 在 2.6 版本引入,但其底层实现还是用多路复用模拟了异步 IO,性能没有优势

  
本文由传智教育博学谷教研团队发布。

  如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。

  转载请注明出处!

  以上就是IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~(io和nio是什么)的详细内容,想要了解更多 IO、NIO、BIO傻傻分不清吗,让我对象告诉你~~的内容,请持续关注盛行IT软件开发工作室。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: