您现在的位置是:首页 > 正文

浅谈Java中常见的三种I/O模型

2024-04-01 05:09:28阅读 5

BIO (Blocking I/O)

BIO 属于同步阻塞 IO 模型

同步阻塞IO模型中,应用程序发起read调用后,会一直阻塞,内核经过准备数据、数据就绪直到数据拷贝到用户空间。
在客户端连接数量不高的情况下,是没什么问题的。但是当面对数十万、百万级别的连接时,传统的BIO模型是无能为力的。
其特点是模式简单,使用方便,并发处理能力低。在这里插入图片描述

NIO (Non-blocking/New I/O)

NIO属于IO多路复用模型、同步非阻塞IO模型

NIO和IO的核心区别:
NIO是以块的方式处理数据,但是IO是以最基础的字节流的形式去写入和读出的。所以效率上的话,肯定是NIO效率比IO效率会高出很多。
NIO不是和IO一样用OutputStream和InputStream 输入流的形式来进行处理数据的,但是又是基于这种流的形式,而是采用了通道和缓冲区的形式来进行处理数据的。
还有一点就是NIO的通道是可以双向的,但是IO中的流只能是单向的。

Java中的NIO于Java1.4中引入,对应java.nio包,提供了 Channel(双向通道),Selector(多路复用器),Buffer(缓冲区)等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。对于高负载、高并发的(网络)应用,应使用NIO。
在这里插入图片描述
在同步非阻塞IO模型中,应用程序会一直发起read调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到返回数据拷贝完成。
相比于同步阻塞IO模型,同步非阻塞IO模型确实有了很大改进。通过轮询操作,避免了一直阻塞。
但是,这种IO模型同样存在问题:应用程序不断进行I/O系统调用轮询数据是否已经准备好的过程是十分消耗CPU资源的。
于是,I/O多路复用模型就上场了。
在这里插入图片描述

IO多路复用模型中,线程首先发起select调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起read调用。read调用的过程(数据从内核空间->用户空间)还是阻塞的。

目前支持IO多路复用的系统调用,有select,epoll等等。select系统调用,是目前几乎在所有操作系统上都有支持

  • select调用:内核提供的系统调用,它支持一次查询多个系统调用的可用状态。几乎所有的操作系统都支持
  • epoll调用:linux2.6内核,属于select调用的增强版本,优化了IO的执行效率。

IO多路复用模型,通过减少无效的系统调用,减少了对CPU资源的消耗。
Java中的NIO,有一个非常重要的选择器(Selector)的概念,也可以被称为多路复用器。通过它,只需要一个线程便可以管理多个客户端连接。当客户端数据到了之后,才会为其服务。
注意,select是阻塞的,无论是通过操作系统的通知(epoll)还是不停的轮询(select,poll),这个函数是阻塞的。所以你可以放心大胆地在一个while(true)里面调用这个函数而不用担心CPU空转。在这里插入图片描述

NIO存在的问题

使用NIO != 高性能,当连接数<1000,并发程度不高或者局域网环境下NIO并没有显著的性能优势。NIO并没有完全屏蔽平台差异,它仍然是基于各个操作系统的I/O系统实现的,差异仍然存在。使用NIO做网络编程构建事件驱动模型并不容易,陷阱重重。
当前成熟的NIO框架,如Netty,MINA等。解决了很多NIO的陷阱,并屏蔽了操作系统的差异,有较好的性能和编程模型。

NIO给我们带来了些什么:

  • 事件驱动模型
  • 避免多线程
  • 单线程处理多任务
  • 非阻塞I/O,I/O读写不再阻塞,而是返回0
  • 基于block的传输,通常比基于流的传输更高效
  • 更高级的IO函数,zero-copy
  • IO多路复用大大提高了Java网络应用的可伸缩性和实用性

AIO (Asynchronous I/O)

AIO 也就是 NIO 2。Java 7 中引入了 NIO 的改进版 NIO 2,它是异步 IO 模型。

异步IO是基于事件和回调机制实现的。也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。在这里插入图片描述

目前来说AIO的应用还不广泛。Netty之前也尝试使用过AIO,但后来因为Netty使用了AIO之后,在Linux系统上的性能并没有多少提升。

最后一张图简单总结一下Java中的BIO、NIO、AIO在这里插入图片描述

网站文章