Java NIO浅析

一、Java有哪些IO方式

传统的java.Io包,基于流模型实现,提供了我们最熟悉的一些功能,包括File抽象,输入输出流等。交互方式是同步、阻塞的方式。比如在读取输入流时,在输入流就绪之前,线程会一直阻塞在哪里。它们之间的调用是可靠的线性调用。java.Io包的优点是比较简单,缺点是IO效率和扩展性存在局限性,容易成为应用性能瓶颈。

在Java1.4中引入了NIO框架(Java.nio包),提供了Selector、channel、buffer等抽象。可以构建同步、非阻塞的应用程序。同时提供了更接近操作系统底层的数据操作方式。

在Java7中,NIO有了进一步的改进,也就是NIO2,引入了异步非阻塞IO方式,也有许多人叫它AIO(Asnychronous IO)。异步IO引入了事件和回调机制。可以理解为,应用操作直接返回,而不会阻塞在哪里,当后台处理完成,操作系统会通知相应线程进行后续工作。

二、NIO的主要组成部分

  • Buffer,高效的数据容器,处理Boolean,其他原始数据类型都提供了相应Buffer实现
  • channel,类似于Linux系统上的文件描述符,是NIO被用来支持批量式IO操作的一种抽象。File和Socket,可以被认为是一种高层次的抽象,而channel是一种更底层的抽象。可以利用操作系统的底层机制,获得特定场景的性能优化。
  • Selector,是NIO实现多路复用的基础。它提供了一种机制,可以检测到注册在Selector上的多个channel是否有channel处于就绪状态,进而实现了单个线程对多个channel的处理,即多路复用。Selector依赖于操作系统的底层实现,在Linux系统上,Selector依赖于epoll函数。

三、NIO能解决什么问题

对于一个应用服务器,当连接数较少的时候,可以使用BIO+线程池模型来处理IO问题。而且这个模型比较简单,也不同过多考虑系统的过载,限流问题。但是这个模型的问题在于严重依赖于线程,需要多线程来处理多任务,而线程是很贵的资源。主要体现在:

  • 线程的创建和销毁成本很高,在Linux系统中线程本质是一个进程,创建和销毁都需要调用重量级的函数
  • 线程本身占用较大内存,像Java的线程栈,一般都会分配512kb-1M的内存空间,如果线程过千,整个JVM的内存都会被吃掉一大半
  • 线程的切换成本很高。线程切换时操作系统首先保存线程上下文,然后执行系统调用,如果线程数过高,可能导致线程切换时间大于线程执行时间,导致系统几乎陷入不可用的状态。

当面对数十万甚至上百万的连接是,传统的BIO是无能为力的,这时候就需要用到一种更高效的IO模型。NIO提供了多路复用机制,为解决问题提供了一种思路。

四、NIO使用步骤

  • 首先,通过Selector.open()创建一个Selector,作为类似调度员的角色。
  • 然后,创建一个ServerSocketChannel,并且向Selector注册。
  • Selector阻塞在select操作,当有channel发生接入请求,就会被唤醒
  • 通过ServerSocketChannel和Buffer,进行数据操作

可以看到,NIO利用了单线程轮询事件的机制,通过高效地定位就绪的channel,来决定做什么,仅仅select阶段是阻塞的(轮询阶段没有可干的事情必须要阻塞)。可以有效避免大量客户端连接时线程的频繁切换问题,应用的扩展性有了非常大的提高。

五、Proactor与Reactor

一般情况下,IO复用机制需要用到时间分发器(event dispatcher)。事件分发器的作用,就是把事件源分给时事件的处理者。就像送快递的在楼下喊:谁谁谁的快递到了,块来拿吧。开发人员在开始的时候需要在分发器哪里注册感兴趣的时间,并提供相应的处理者(event handler),或者是回调函数。事件分发器在适当的时候,会将请求的事件分发该处理者或回调函数。

事件分发器的两种模式成为Reactor和Proactor。Reactor模式是基于同步IO的,而Proactor模式是和异步IO相关的。在Reactor模式下,事件分发器等待某个事件可用或某个操作的状态发生,事件分发器就把这个事件传给实现注册的事件处理器或回调函数,由后者来做事件的读写操作。而在Proactor模式中,事件处理者直接发起一个异步读写操作,而实际的工作是由操作系统来完成的。时间分发器得知了这个请求,它默默等待请求的完成,然后转发完成事件给时间处理器或回调函数。

六、NIO带来的好处

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

七、NIO的缺陷

  • 使用NIO!=高性能,并发程度不高或局域网环境下NIO并没有显著的性能优势
  • NIO并没有完全屏蔽平台差异,它依然是基于各个操作系统的IO实现的,差异仍然存在
  • 推荐大家使用成熟的NIO框架,如Netty,解决了很多NIO的缺陷,并屏蔽了操作系统的差异,有较好性能和编程模型
  • NIO不适合数据量太大交互的场景。对于多路复用IO,当有新的IO请求出现在数据拷贝阶段,如果拷贝数据量很大会导致线程长期阻塞,最后造成性能瓶颈的情况

八、BIO、NIO、AIO使用场景

  • BIO方式适用于连接数目比较少且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序比较简单
  • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持
  • AIO方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持

参考文章:https://tech.meituan.com/2016/11/04/nio.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值