java nio 缺陷_Java NIO 是 NIO么?

本文转自“雨夜随笔”公众号,欢迎关注Java NIO 是 NIO么?​mp.weixin.qq.comf4aca02c3fbc06dc5beb3285c59d2434.png

网上目前很多文章,都是直接将Java NIO说成是非阻塞IO,而且不乏很多大厂的文章,或者热点文章。但是我们要首先意识到这种说法是错误的。至于为什么,让我们来看一下:

I/O

I/O在程序中指的 Input/Output,也就是数据的输入和输出。程序的I/O操作依赖于底层的I/O操作,基本上都会用上底层的read/write操作。而对于read和write,需要知道的是read不是直接将数据直接从物理设备读取到内存中,而write也不是将数据直接从内存写入到物理设备。上层应用无论是调用系统的read还是write,都涉及到内核缓存区,具体来说,调用操作系统的read,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用,是把数据从进程缓冲区复制到内核缓冲区。如下图:

缓存区的设置主要是为了减少频繁地与设备的物理交换。因为外部设备的直接读写,涉及操作系统的中断。发生系统中断时,需要保存之前的进程数据和状态等信息,而结束中断之后,还需要恢复之前的进程数据和状态等信息。为了减少这种底层系统的时间损耗、性能损耗,于是出现了内存缓冲区。

而缓存区的出现,导致实际上的I/O操作涉及到两个阶段:等待数据准备好

将数据在内存缓存区之间进行拷贝

我们先记住这两个阶段,一般来说IO模型主要是在这两个阶段有区别。

Unix中的I/O

在了解Unix中的I/O之前,我们先了解两组名词:阻塞和非阻塞、同步和异步。阻塞:阻塞意味着调用方在结果出现之前,不会做任何其他事情。

非阻塞:非阻塞意味着调用方在结果出现之前,同时在做其他的事情。被调用方会立即返回一个值,调用方拿到后可以做其他事情也可以选择不做。

同步:必须等待被调用方处理完请求返回结果。

异步:被调用方处理请求通过调用方注册的回调接口返回结果。

网上有各种各样的定义,根据我自己的理解,我认为阻塞和非阻塞主要针对调用方,它们的区别在于阻塞在得到结果前会一直等待,非阻塞会先得到一个值,但是在得到真正的结果前可以做其他的事情。而同步和异步主要针对被调用方的结果返回形式,同步是处理完再返回,异步是处理完后通过回调返回结果。

I/O模型

上面两两组合,构成了Unix中的四种I/O模型。

同步阻塞I/O (Blocking IO)

在阻塞式IO模型中,应用程序从IO系统调用开始,直到系统调用返回,在这段时间内,调用进程是阻塞的。如下图:

阻塞IO的优点是:实现简单,而且阻塞期间,用户线程基本不会占用资源。但是缺点也很明显:那就是在高并发的场景下,需要大量的线程来维护每一个阻塞的任务,内存和线程切换的开销都非常大。

同步非阻塞I/O (Non-Blocking IO)

在Linux系统下,可以设置socket为非阻塞的的模式,在这种模式下,如果调用时没有数据,系统会立即返回一个调用失败的消息。然后调用方过段时间再查询,如果有数据,则变成阻塞方式,进行数据复制。而这个就是我们日常所说的NIO。如下图:

非阻塞IO的优点在于:每次的调用都可以立即得到反馈,调用方不会阻塞,实时性比较好。缺点是:调用方需要不断的轮询,这个会占用CPU资源,并且效率低下。

I/O多路复用 (IO Multiplexing)

为了避免非阻塞IO模型上轮询的问题,系统设计了select/epoll,在这个模式下,一个进程可以监控多个文件描述符,也就是多个数据的就绪状态。目前支持IO多路复用的系统调用有select,epoll等,select基本在所有的系统都支持,epoll是Linux 2.6内核提出的,是select的一种增强版本。流程如下:

IO多路复用模型是在非阻塞IO上的升级,优点是:不需要单独维护不同的连接,通过select/epoll可以通过一个选择器查询线程来处理上千万个连接。缺点是:IO多路复用本质上还是属于同步IO,也就是数据就绪后,还是需要阻塞等待数据复制完成。

异步I/O (Asynchronous IO)

异步IO又称为AIO,基本流程和上面不同,在于被调用方完成所有的操作后,再通知调用方。调用方得到通知后可以直接进行后续操作,而不需进行等待,如下图:

异步IO可以说是非常美好,有点非常明显,那就是:调用方永远不会阻塞。缺点是:因为被调用方完成了大部分工作,所以需要被调用方支持。目前Windows下有IOCP实现了异步IO,Linux下在2.6引入,但是还不完善,并且底层依旧采用epoll,和IO多路复用相比,性能上没有明星的优势。

Java NIO

在上面我们说了NIO指的是Non-Blocking IO,也就是非阻塞IO。那么Java NIO是不是就是非阻塞IO呢?也就是当面试官问你:请你说一下Java的NIO。你这时候直接说起了非阻塞IO是不是可以呢? ** 事实上,Java NIO的实际全程是(Java New IO),主要是对为了弥补之前IO(OIO)同步阻塞的缺点,相比于老的IO面向流,NIO是面向缓冲区的。

Java NIO的核心由三部分组成:Channel:通道

Buffer: 缓冲区

Selector:选择器

有选择器,我们对比上面的四种模型就了解到Java NIO主要是基于IO多路复用这个IO模型。我们可以看一下Java NIO的模型:

我们这里限于篇幅,不细讲Java NIO的实现细节,我们主要讲一下Java NIO的设计哲学。

传统的OIO是面向字节流或者字符流的,总是以流式的方式顺序从流中读取字节。这样有个问题就是不能随意更改读取指针的位置,而NIO中为了解决这个问题,引入了Channel和Buffer。读取和写入,只是从Channel中读取到Buffer里,或者从Buffer写入到Channel中。由于不是顺序操作,所以可以随意更改读取的位置。

传统的OIO因为是同步阻塞的,例如,传统的IO操作read,当我们读取一个文件时,我们read的线程就会被阻塞住,直到read操作完成。那么NIO为了做到非阻塞,当我们调用read操作时,如果没有数据,read会直接返回,不会阻塞当前线程。因为NIO使用了Channel和Channel的多路复用技术(IO多路复用),后续可以通过Selector获取数据的就绪信息。

总结

Java NIO是Java New IO的简称,是为了弥补之前IO的缺点。底层主要是基于IO多路复用的IO模型,并不简简单单的是NIO(非阻塞IO)。所以如果下一次面试官问你Java NIO和NIO的区别是什么?你要意识到他更希望你说出IO多路复用的细节和Java NIO中的Buffer, Channel和Selector以及他们的工作流程。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值