java时间模型_JAVA IO模型

BIO、NIO、AIO、Selector、Poll、EPoll、Reactor、Proactor这些名词如果仔细去研究,是比较容易混淆的,相关联但有所区别,或者是在不同的领域或者层级,很多文章没有做明确的区分,做些梳理希望对理解有帮助(如有错请纠偏)。

一、概念铺垫

1.用户空间与内核空间

操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。

2.应用到操作系统内核

把java应用和操作系统的文件(一切皆文件)调用,拆开看,大概的操作如下:

【JAVA应用】-->【本地方法】-->【操作系统内核方法】-->【文件】

3.IO模式两阶段

1. 等待数据准备 (Waiting for the data to be ready)---内核

2. 将数据从内核拷贝到进程中 (Copying the data from the kernelto the process)--用户空间

(ps:模式是对于普遍问题的普遍解决方案。)_

bb6d46cf32a654478b3c115947de0c15.png

4.什么是IO

unix/linux的世界里,不管Socket、管道、终端,一切都是文件,一切都是二进制流。

I/O操作(input and output),指在信息交换的过程中,对这些流进行数据的收发操作。

内核(kernel)利用文件描述符FD(file descriptor)来访问文件。

二、BIO、NIO、AIO

1.系统层面IO模式

linux系统提供了下面五种网络模式的方案。

- 阻塞 I/O(blocking IO)

- 非阻塞 I/O(nonblocking IO)

- I/O 多路复用( IO multiplexing)

- 信号驱动 I/O( signal driven IO)---不常用

- 异步 I/O(asynchronous IO)---2.6 版本内核的一个标准特性

2.阻塞 I/O

阻塞 IO 分为两个步骤:

步骤 1. 等待数据准备,拷贝到 OS 内核缓存区 (该过程中应用程序进程都会被阻塞)

fd20d7f1a41a0d89288fa1f7ee45a5de.png

3.非阻塞 IO

对比BIO的步骤 1 等待数据的过程中,应用程序进程不会被阻塞,而是不断询问 OS 内核数据有没有准备好:

如果数据没有准备好,OS 内核返回一个 error,应用程序进程过一段时间再次询问(该过程中应用程序进程不会被阻塞)

PS:Java NIO 中可以设置 SocketChannel:channel.configureBlocking(false);

621c56758a1311e29667d489a726f3cf.png

3.多路复用IO

多路复用IO就是我们说的select,poll,epoll,通过一种机制,让一个进程process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。

51f1166143e445837508a303a97389bc.png

说说区别:

1.支持一个进程所能打开的最大连接数

select

单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是32*32,同理64位机器上FD_SETSIZE为32*64),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。

poll

poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的

epoll

虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接

2.FD剧增后带来的IO效率问题

select

因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。

poll

同上

epoll

因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。

3.消息传递方式

select

内核需要将消息传递到用户空间,都需要内核拷贝动作

poll

同上

epoll

epoll通过内核和用户空间共享一块内存来实现的,利用mmap()文件映射内存加速与内核空间的消息传递;减少复制开销。

总结:

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特点。

1.表面上看epoll的性能最好,但是在连接数少并且连接都十分活跃的情况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制需要很多函数回调。

2.select低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善。

4.异步 IO Asynchronous IO

异步IO又叫做事件驱动IO,异步IO操作是需要操作系统底层支持。

1.用户进程发起异步 read 操作后,OS 内核立即返回,用户进程不会阻塞,而是去做其他事情。

这两个阶段都是由内核自动完成的,然后发送一个信号告知用户线程操作已经完成。

三、总结

1.5种IO模型

1.同步与异步

数据从“内核缓存区-用户内存”这个过程是否需要用户进程等待,实际IO读写是否阻塞请求进程。

是否等待IO执行的结果,使用异步IO来编写程序性能会远远高于同步IO,但异步IO的缺点是编程模型复杂。

2.阻塞与非阻塞

阻塞与非阻塞关注的是调用者在等待结果返回之前所处的状态

阻塞与非阻塞描述的是用户线程调用内核IO操作的方式

阻塞blocking是指IO操作需要彻底完成后才返回到用户空间,调用结果返回之前调用者被挂起。

非阻塞noblocking是指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成。

阻塞与非阻塞是函数或方法的实现方式,在数据就绪之前是立即返回还是等待,发起IO请求是否会被阻塞。

3.概念总结

Selector、Poll、EPoll是lunix操作系统的内核多路复用IO的概念。

BIO(同步阻塞)、NIO(同步非阻塞)、AIO(异步非阻塞)是更多的是JAVA的概念,JAVA基于操作系统的IO模式,实现IO,提供IO的基础包。

很多JAVA框架基于线程模式,如Proactor、Reactor更多的可用理解为JAVA多线程的实现用的设计模式。这个有点类似操作系统实现的Selector、Poll、EPoll,但是可用基于操作系统这些模式提供的接口,再应用上层使用。

这些也可以统称IO模式,理解为软件上实现IO功能的一种设计,一种解决方案,模式是对于普遍问题的普遍解决方案。

4.JAVA IO包

BIO 就是传统的java.io包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的优点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。

NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。

AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它 AIO(Asynchronous IO),异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

IO 大致可以分为4种类型:

InputStream、OutputStream 基于字节操作的 IO

Writer、Reader 基于字符操作的 IO

File 基于磁盘操作的 IO

Socket 基于网络操作的 IO

java.net下提供的 Scoket 很多时候人们也把它归为 同步阻塞 IO ,因为网络通讯同样是 IO 行为。

java.io下的类和接口很多,但大体都是 InputStream、OutputStream、Writer、Reader 的子集,所有掌握这4个类和File的使用,是用好 IO 的关键。

5.高性能IO设计模式

Reactor

在Reactor模式中会先对每个客户端注册感兴趣的事件,然后有一个线程专门去轮询每个客户端是否有事件发生,当有事件发生时便顺序处理每个事件,当所有事件都处理完毕后便再转去继续轮询。

Proactor

在Proactor模式中当检测到有事件发生时会新起一个异步操作,然后交由内核线程去处理,当内核线程完成IO操作之后,发送一个通知告知操作已经完成。IO模型中的异步IO模型采用的就是Proactor模式。

6.参考

Linux IO模式及 select/poll/epoll详解:https://segmentfault.com/a/1190000003063859

Linux IO 模型:https://www.jianshu.com/p/fb153133abef

IO模型:https://www.jianshu.com/p/39e2281d9864

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值