作者:公众号:我是攻城师

前言

Java里面的IO模型种类较多,主要包括BIO,NIO和AIO,每个IO模型都有不一样的地方,那么这些IO模型是如何演变呢,底层的原理又是怎样的呢? 本文我们就来聊聊。

BIO

BIO全称是Blocking IO,是JDK1.4之前的传统IO模型,本身是同步阻塞模式,针对网络通信都是一请求一应答的方式,虽然简化了上层的应用开发,但在性能和可靠性方面存在着巨大瓶颈,试想一下如果每个请求都需要新建一个线程来专门处理,那么在高并发的场景下,机器资源很快就会被耗尽,当然,我们可以通过线程池来优化这种情况,但即使是这样,仍然改变不了阻塞IO的根本问题,就是在IO执行的两个阶段都被block了。拿一个read操作来举例子,在linux中,应用程序向linux发起read操作,会经历两个步骤:

第一个阶段linux内核首先会把需要读取的数据加载到操作系统内核的缓冲区中(Linux文件系统是缓存IO,也称标准IO)

第二个阶段应用程序拷贝内核里面的数据到自己的用户空间中

如果是socket操作,类似也会经历两个步骤:

第一个阶段:通常涉及等待网络上的数据分组包到达,然后被复制到内核的缓冲区

第二个阶段:把数据从内核缓冲区,从内核缓冲区拷贝到用户进程的内存空间里面

同步阻塞IO之所以效率低下,就是因为在这两个阶段,用户的线程或者进程都是阻塞的,期间虽然不占cpu资源,但也意味着该线程也不能再干其他事。有点站着茅坑不拉屎的感觉,自己暂时不用了,也不让别人用。

图示如下:

NIO

由于BIO的缺点,导致Java在JDK1.0至JDK3.0中,网络通信模块的性能一直是短板,所以很多人更倾向于使用C/C++开发高性能服务端。为了强化Java在服务端的市场,终于在JSR-51也就是JDK4.0的时候发布了Java NIO,可以支持非阻塞IO。并新增了java.nio的包,提供很多异步开发的API和类库。

主要的类和接口如下:

(1)进行异步IO操作的缓冲区ByteBuffer

(2)进行异步IO操作的管道Pipe

(3)进行各种IO操作的Channel,主要包括ServerSocketChannel和SocketChannel

(4)实现非阻塞IO的多路复用器Selector

NIO主要有buffer、channel、selector三种技术的整合,通过零拷贝的buffer取得数据,每一个客户端通过channel在selector(多路复用器)上进行注册。服务端不断轮询channel来获取客户端的信息。channel上有connect,accept(阻塞)、read(可读)、write(可写)四种状态标识。根据标识来进行后续操作。所以一个服务端可接收无限多的channel。不需要新开一个线程。大大提升了性能。

新的nio类库,促进了异步非阻塞编程的发展和应用,但仍然有一些不足之处:

(1)没有统一的文件属性,例如读写权限

(2)api能力比较弱,例如目录的及联创建和递归遍历,往往需要自己完成。

(3)底层操作系统的一些高级API无法使用

(4)所有的文件操作都是同步阻塞调用,在操作系统层面上并不是异步文件读写操作。

Java里面的NIO其实采用了多路复用的IO模式,多路复用的模式在Linux底层其实是采用了select,poll,epoll的机制,这种机制可以用单个线程同时监听多个io端口,当其中任何一个socket的数据准备好了,就能返回通知用户线程进行读取操作,与阻塞IO阻塞的是每一个用户的线程不一样的地方是,多路复用只需要阻塞一个用户线程即可,这个用户线程通常我们叫它Selector,其实底层调用的是内核的select,这里面只要任何一个IO操作就绪,就可以唤醒select,然后交由用户线程处理。用户线程读取数据这个过程仍然是阻塞的,多路复用技术只是在第一个阶段可以变为非阻塞调用,但在第二个阶段拷贝数据到用户空间,其实还是阻塞的,多路复用技术的最大特点是使用一个线程就可以处理很多的socket连接,尽管性能上不一定提升,但支持并发能力却大大增强了。

图示如下:

AIO

AIO,其实是NIO的改进优化,也被称为NIO2.0,在2011年7月,也就是JDK7的版本中发布,它主要提供了三个方面的改进:

(1)提供了能够批量获取文件属性的api,通过SPI服务,使得这些API具有平台无关性。

(2)提供了AIO的功能,支持基于文件的异步IO操作和网络套接字的异步操作

(3)完成了JSR-51定义的通道功能等。

AIO 通过调用accept方法,一个会话接入之后再次调用(递归)accept方法,监听下一次会话,读取也不再阻塞,回调complete方法异步进行。不再需要selector 使用channel线程组来接收。

从NIO上面我们能看到,对于IO的两个阶段的阻塞,只是对于第一个阶段有所改善,对于第二个阶段在NIO里面仍然是阻塞的。而真正的理想的异步非阻塞IO(AAIO)要做的就是,将IO操作的两个阶段都全部交给内核系统完成,用户线程只需要告诉内核,我要读取一块数据,请你帮我读取,读取完了放在我给你的地址里面,然后告诉我一声就可以了。

AIO可以做到真正的异步的操作,但实现起来比较复杂,支持纯异步IO的操作系统非常少,目前也就windows是IOCP技术实现了,而在Linux上,目前有很多开源的异步IO库,例如libevent、libev、libuv,但基本都不是纯的异步IO操作,底层还是是使用的epoll实现的。

图示如下:

NIO与Netty

既然Java拥有了各种IO体系,那么为什么还会出现Netty这种框架呢?

Netty出现的主要原因,如下:

(1)Java NIO类库和API繁杂众多,使用麻烦。

(2)Java NIO封装程度并不高,常常需要配合Java多线程编程来使用,这是因为NIO编程涉及到Reactor模式。

(3)Java NIO异常体系不完善,如客户端面临断连,重连,网络闪断,半包读写,网络阻塞,异常码流等问题,虽然开发相对容易,但是可靠性和稳定性并不高。

(4)Java NIO本身的bug,修复较慢。

注意,真正的异步非阻塞io,是需要操作系统层面支持的,在windows上通过IOCP实现了真正的异步io,所以Java的AIO的异步在windows平台才算真正得到了支持,而在Linux系统中,仍然用的是epoll模式,所以在Linux层面上的AIO,并不是真正的或者纯的异步IO,这也是Netty里面为什么采用Java的NIO实现的,而并非是AIO,主要原因如下:

(1)AIO在linux上底层实现仍使用EPOLL,与NIO相同,因此在性能上没有明显的优势

(2)Windows的AIO底层实现良好,但Netty的开发者并没有把Windows作为主要使用平台,所以优化考虑Linux

总结

本文主要介绍了Java里面IO模型的演变和发展,这也是Java在服务端领域大放异彩的一个重要原因,了解这些知识之后,我们再去学习高性能的Netty框架,将会更加容易。

Java的BIO,NIO和AIO的区别于演进的更多相关文章

  1. BIO,NIO与AIO的区别

    Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理.Java AIO(NIO.2 ...

  2. Java BIO NIO 与 AIO

    回顾 上一章我们介绍了操作系统层面的 IO 模型. 阻塞 IO 模型. 非阻塞 IO 模型. IO 复用模型. 信号驱动 IO 模型(用的不多,知道个概念就行). 异步 IO 模型. 并且介绍了 IO ...

  3. 以Java的视角来聊聊BIO、NIO与AIO的区别?

    转: 以Java的视角来聊聊BIO.NIO与AIO的区别? 飞丫玲丫 17-07-2623:10 题目:说一下BIO/AIO/NIO 有什么区别?及异步模式的用途和意义? BIO(Blocking I ...

  4. [转帖]JAVA BIO与NIO、AIO的区别(这个容易理解)

    JAVA BIO与NIO.AIO的区别(这个容易理解) https://blog.csdn.net/ty497122758/article/details/78979302 2018-01-05 11 ...

  5. BIO与NIO、AIO的区别(这个容易理解)

    转自:http://blog.csdn.net/skiof007/article/details/52873421 BIO与NIO.AIO的区别(这个容易理解) IO的方式通常分为几种,同步阻塞的BI ...

  6. JAVA BIO与NIO、AIO的区别

    IO的方式通常分为几种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO. 一.BIO 在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSock ...

  7. 【转】JAVA BIO与NIO、AIO的区别

    Java中IO的模型分为三种,同步阻塞的BIO.同步非阻塞的NIO.异步非阻塞的AIO. BIO[同步阻塞] 在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个Ser ...

  8. Java中BIO、NIO、AIO的区别和应用场景

    学习IO,首先要明白四个东西. 1.同步            java自己去处理io. 2.异步          java将io交给操作系统去处理,告诉缓存区大小,处理完成回调. 3.阻塞     ...

  9. JAVA 中BIO,NIO,AIO的理解

    [转自]http://qindongliang.iteye.com/blog/2018539 ?????????????????????在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解 ...

随机推荐

  1. MAVEN工程相关配置

    MAVEN工程插件安装: Name: MavenArchiver Location: https://repo1.maven.org/maven2/.m2e/connectors/m2eclipse- ...

  2. PIL库学习及运用

    了解PIL以及安装. 个方面的功能: (1) 图像归档:对图像进行批处理.生产图像预览.图像格式转换等. (2) 图像处理:图像基本处理.像素处理.颜色处理等. 安装PIL在cmd中输入 pip in ...

  3. ARM指令集详解

    一.跳转指令 B: 跳转指令 BL: 带返回的跳转指令 BLX: 带返回和状态切换的跳转指令 BX: 带状态切换的跳转指令 二.数据处理指令 1.MOV:数据传送指令 MOV{条件}{S}    目的 ...

  4. Vue 获取元素样式 元素高度

    看到这个问题我第一时间想的竟然是JS 不知道你是怎么想的 不过昨天有一个小哥哥 问我一个Vue的 哈哈哈 get了 我当时问他为什么不用JS获取 他说 这个性能更高 那我们来看看这个高性能的获取元素高 ...

  5. python对mysql数据库的一些常用操作

    import pymysql class OperationDatabase(): def __init__(self,Ip,User,PassWd,DBname): self.ip=Ip self. ...

  6. ORM学员管理系统

    1.使用MySQL自己创建一个数据库,以下例为例 CREATE DATABASE orm DEFAULT CHARSET utf8 COLLATE utf8_general_ci; 2.在项目的set ...

  7. Spring @Value取值为null或@Autowired注入失败

    @Value 用于注入.properties文件中定义的内容 @Autowired 用于装配bean 用法都很简单,很直接,但是稍不注意就会出错.下面就来说说我遇到的问题. 前两天在项目中遇到了一个问 ...

  8. github分支规范

    转自:https://www.cnblogs.com/xuld 一.目的 我们制定分支规范,意在实现以下目标: 减少沟通成本:开发者可以很清晰地知道需要修改的代码位于哪个分支. 减少 bug 隐患:避 ...

  9. 《C++实践之路.pdf》源码

    > 源码下载方法 < >> 打开微信 >> 扫描下方二维码 >> 关注林哥私房菜 >> 输入对应编号获取百度网盘提取密码 全书源码[已更新完 ...

  10. bittorrent 学习(四) tracker peer通讯

    看看 tracker.c文件 http_encode() 为http发送进行编码转换 int http_encode(unsigned char *in,int len1,char *out,int ...