I/O模型之四:Java 浅析I/O模型(BIO、NIO、AIO、Reactor、Proactor)
目录:
《I/O模型之二:Linux IO模式及 select、poll、epoll详解》
《I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor》
一、传统的BIO
网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建立连接,如果连接建立成功,双方就可以通过网络套接字(Socket)进行通信。
在基于传统同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口,Socket负责发起连接操作,连接成功之后,双方通过输入和输出流进行同步阻塞式通信。
1.1、BIO通信模型图
首先,我们通过下面的通信模型图来熟悉下BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。

该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,由于线程是JAVA虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能将急剧下降,随着并发访问量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。
它的弊端有很多:
1.性能问题:一连接一线程模型导致服务端的并发接入数和系统吞吐量受到极大限制;
2.可靠性问题:由于I/O操作采用同步阻塞模式,当网络拥塞或者通信对端处理缓慢会导致I/O线程被挂住,阻塞时间无法预测;
3.可维护性问题:I/O线程数无法有效控制、资源无法有效共享(多线程并发问题),系统可维护性差;
二、伪异步IO编程
为了解决同步阻塞IO面临的一个链路需要一个线程处理的问题,后来有人对它的线程模型进行了优化,后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,其中M可以远远大于N,通过线程池可以灵活的调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽。 下面,我们结合连接模型图和源码,对伪异步IO进行分析,看它是否能够解决同步阻塞IO面临的问题。
2.1、伪异步IO模型图
采用线程池和任务队列可以实现一种叫做伪异步的IO通信框架,它的模型图如下:

当有新的客户端接入的时候,将客户端的Socket封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK的线程池维护一个消息队列和N个活跃线程对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。
三、NIO(多路复用器Selector)
Java NIO的实现关键是通过多路复用IO技术实现的,多路复用的核心就是通过Selector来轮询注册在其上的Channel,当发现某个或者多个Channel处于就绪状态后,从阻塞状态返回就绪的Channel的选择键集合,进行IO操作。由于多路复用器是NIO实现非阻塞IO的关键,它又是主要通过Selector实现。


选择器使得我们能够通过较少的线程便可监视许多连接上的事件。
详细见《Java NIO系列教程(一) Java NIO 概述》
四、AIO异步IO

五、五种IO模型
在《Unix网络编程》一书中提到了五种IO模型,分别是:阻塞IO、非阻塞IO、多路复用IO、信号驱动IO以及异步IO。
六、两种I/O多路复用模式:Reactor和Proactor
因此便出现了下面的两种高性能IO设计模式:Reactor(反应堆)和Proactor(前摄器)。
见《I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor》和《Netty中的三种Reactor(反应堆)》
在Reactor模式中,会先对每个client注册感兴趣的事件,然后有一个线程专门去轮询每个client是否有事件发生,当有事件发生时,便顺序处理每个事件,当所有事件处理完之后,便再转去继续轮询,如下图所示:

从这里可以看出,上面的五种IO模型中的多路复用IO就是采用Reactor模式。注意,上面的图中展示的 是顺序处理每个事件,当然为了提高事件处理速度,可以通过多线程或者线程池的方式来处理事件。
可以看jdk1.8的源码,java的nio采用的是epoll(linux,window是IOCP),也即是Reactor模式:
package sun.nio.ch;
public class DefaultSelectorProvider {
//...
/**
* Returns the default SelectorProvider.
*/
public static SelectorProvider create() {
String osname = AccessController
.doPrivileged(new GetPropertyAction("os.name"));
if (osname.equals("SunOS"))
return createProvider("sun.nio.ch.DevPollSelectorProvider");
if (osname.equals("Linux"))
return createProvider("sun.nio.ch.EPollSelectorProvider");
return new sun.nio.ch.PollSelectorProvider();
}
//...
}
更详细的分析见:https://zhuanlan.zhihu.com/p/27441342?group_id=859562548406677504
在Proactor模式中,当检测到有事件发生时,会新起一个异步操作,然后交由内核线程去处理,当内核线程完成IO操作之后,发送一个通知告知操作已完成,可以得知,异步IO模型采用的就是Proactor模式。
见:
《I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor》
《【转】第8章 前摄器(Proactor):用于为异步事件多路分离和分派处理器的对象行为模式》
I/O模型之四:Java 浅析I/O模型(BIO、NIO、AIO、Reactor、Proactor)的更多相关文章
- (转)也谈BIO | NIO | AIO (Java版)
原文地址: https://my.oschina.net/bluesky0leon/blog/132361 关于BIO | NIO | AIO的讨论一直存在,有时候也很容易让人混淆,就我的理解,给出一 ...
- 也谈BIO | NIO | AIO (Java版--转)
关于BIO | NIO | AIO的讨论一直存在,有时候也很容易让人混淆,就我的理解,给出一个解释: BIO | NIO | AIO,本身的描述都是在Java语言的基础上的.而描述IO,我们需要从两个 ...
- I/O模型系列之三:IO通信模型BIO NIO AIO
一.传统的BIO 网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请 ...
- java BIO/NIO/AIO 学习
一.了解Unix网络编程5种I/O模型 1.1.阻塞式I/O模型 阻塞I/O(blocking I/O)模型,进程调用recvfrom,其系统调用直到数据报到达且被拷贝到应用进程的缓冲区中或者发生错误 ...
- Java提供了哪些IO方式?IO, BIO, NIO, AIO是什么?
IO一直是软件开发中的核心部分之一,而随着互联网技术的提高,IO的重要性也越来越重.纵观开发界,能够巧妙运用IO,不但对于公司,而且对于开发人员都非常的重要.Java的IO机制也是一直在不断的完善,以 ...
- 3. 彤哥说netty系列之Java BIO NIO AIO进化史
你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...
- JAVA中的BIO,NIO,AIO
在了解BIO,NIO,AIO之前先了解一下IO的几个概念: 1.同步 用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪, 例如自己亲自出马持银行卡到银行取钱 2.异步 用户触发IO操作以后, ...
- JAVA bio nio aio
[转自]http://qindongliang.iteye.com/blog/2018539 在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? ...
- JAVA 中BIO,NIO,AIO的理解
[转自]http://qindongliang.iteye.com/blog/2018539 ?????????????????????在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解 ...
随机推荐
- 简单探究Android平台下' if ' 语句条件判断耗时情况
2017年6月13日 前言 前几日在改Bug时看到好多调试时用的日志语句都被一个日志开关控制着它的执行权.形如: if(Constants.LOG_TAG){ Log.d(TAG, "Ini ...
- 使用 dmidecode 查看Linux服务器信息
使用 dmidecode 查看Linux服务器信息 来源 http://www.laozuo.org/6682.html 对于大部分普通服务器用户来说,我们选择VPS.服务器产品的时候比较关心的是产 ...
- Mysql 语句优化
通过 show status 命令了解各个 sql 语句的执行频率格式:Mysql> show [session | global] status;注:session 表示当前连接global ...
- [SNOI2017]一个简单的询问【莫队+容斥原理】
题目大意 给你一个数列,让你求两个区间内各个数出现次数的乘积的和. 分析 数据范围告诉我们可以用莫队过. 我并不知道什么曼哈顿什么乱七八糟的东西,但是我们可以用容斥原理将这个式子展开来. \[\sum ...
- docker-网络基础
网络 Docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络 Docker 安装时会自动在 host 上创建三个网络, ⚡ root@bogon /home ...
- python装饰器中的计时器thd.strat用法
thd = KThread(target=_new_func, args=(), kwargs=new_kwargs) thd.start() thd.join(seconds) alive = th ...
- 蓝桥杯试题 k倍区间(dp)
问题描述 给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间. ...
- LCOW —— 单一Docker引擎下可同时运行Linux和Windows容器啦!
https://blog.csdn.net/m2l0zgssvc7r69efdtj/article/details/79251059 就在上周,Docker官方的master分支上新增了LCOW(Li ...
- vue-router 如何默认显示三级子路由
{ path: '/aaa', name: 'aaa', title: '统计分析', component: () => import('@/aaa.vue'),//一级子组件.容器 child ...
- P1966 火柴排队
这道题需要小小的思考一波 (然而我思考了两节课) 好,我们先得出一个结论:a中第k大的与b中第k大的一定要排在一起,才能保证最小. 然后发现:挪a,b其实没有区别,故我们固定a,挪b. 然后我们就思考 ...