IO、NIO、AIO理解
摘要: 关于BIO和NIO的理解
最近大概看了ZooKeeper和Mina的源码发现都是用Java NIO实现的,所以有必要搞清楚什么是NIO。下面是我结合网络资料自己总结的,为了节约时间图示随便画的,能达意就行。
简介:
BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
AIO(NIO.2):异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
BIO
同步阻塞式IO,相信每一个学习过操作系统网络编程或者任何语言的网络编程的人都很熟悉,在while循环中服务端会调用accept方法等待接收客户端的连接请求,一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成。
如果BIO要能够同时处理多个客户端请求,就必须使用多线程,即每次accept阻塞等待来自客户端请求,一旦受到连接请求就建立通信套接字同时开启一个新的线程来处理这个套接字的数据读写请求,然后立刻又继续accept等待其他客户端连接请求,即为每一个客户端连接请求都创建一个线程来单独处理,大概原理图就像这样: 
虽然此时服务器具备了高并发能力,即能够同时处理多个客户端请求了,但是却带来了一个问题,随着开启的线程数目增多,将会消耗过多的内存资源,导致服务器变慢甚至崩溃,NIO可以一定程度解决这个问题。
NIO
同步非阻塞式IO,关键是采用了事件驱动的思想来实现了一个多路转换器。
NIO与BIO最大的区别就是只需要开启一个线程就可以处理来自多个客户端的IO事件,这是怎么做到的呢?
就是多路复用器,可以监听来自多个客户端的IO事件:
A. 若服务端监听到客户端连接请求,便为其建立通信套接字(java中就是通道),然后返回继续监听,若同时有多个客户端连接请求到来也可以全部收到,依次为它们都建立通信套接字。
B. 若服务端监听到来自已经创建了通信套接字的客户端发送来的数据,就会调用对应接口处理接收到的数据,若同时有多个客户端发来数据也可以依次进行处理。
C. 监听多个客户端的连接请求和接收数据请求同时还能监听自己时候有数据要发送。 
总之就是在一个线程中就可以调用多路复用接口(java中是select)阻塞同时监听来自多个客户端的IO请求,一旦有收到IO请求就调用对应函数处理。
各自应用场景
到这里你也许已经发现,一旦有请求到来(不管是几个同时到还是只有一个到),都会调用对应IO处理函数处理,所以:
(1)NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,Jetty,Mina,ZooKeeper等都是基于java nio实现。
(2)BIO方式适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。
(3)AIO新的IO2.0,即NIO2.0,jdk1.7开始应用,叫做异步不阻塞的IO。AIO引入异常通道的概念,采用了Proactor模式,简化了程序编写,一个有效的请求才启动一个线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间长的应用。
附录:下面附上一个别人写的java NIO的例子。
服务端:
- 1. package cn.nio;
- 2.
- 3. import java.io.IOException;
- 4. import java.net.InetSocketAddress;
- 5. import java.nio.ByteBuffer;
- 6. import java.nio.channels.SelectionKey;
- 7. import java.nio.channels.Selector;
- 8. import java.nio.channels.ServerSocketChannel;
- 9. import java.nio.channels.SocketChannel;
- 10. import java.util.Iterator;
- 11.
- 12. /**
- 13. * NIO服务端
- 14. *
- 15. */
- 16. public class NIOServer {
- 17. //通道管理器
- 18. private Selector selector;
- 19.
- 20. /**
- 21. * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
- 22. * @param port 绑定的端口号
- 23. * @throws IOException
- 24. */
- 25. public void initServer(int port) throws IOException {
- 26. // 获得一个ServerSocket通道
- 27. ServerSocketChannel serverChannel = ServerSocketChannel.open();
- 28. // 设置通道为非阻塞
- 29. serverChannel.configureBlocking(false);
- 30. // 将该通道对应的ServerSocket绑定到port端口
- 31. serverChannel.socket().bind(new InetSocketAddress(port));
- 32. // 获得一个通道管理器
- 33. this.selector = Selector.open();
- 34. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
- 35. //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
- 36. serverChannel.register(selector, SelectionKey.OP_ACCEPT);
- 37. }
- 38.
- 39. /**
- 40. * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
- 41. * @throws IOException
- 42. */
- 43. @SuppressWarnings("unchecked")
- 44. public void listen() throws IOException {
- 45. System.out.println("服务端启动成功!");
- 46. // 轮询访问selector
- 47. while (true) {
- 48. //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
- 49. selector.select();
- 50. // 获得selector中选中的项的迭代器,选中的项为注册的事件
- 51. Iterator ite = this.selector.selectedKeys().iterator();
- 52. while (ite.hasNext()) {
- 53. SelectionKey key = (SelectionKey) ite.next();
- 54. // 删除已选的key,以防重复处理
- 55. ite.remove();
- 56. // 客户端请求连接事件
- 57. if (key.isAcceptable()) {
- 58. ServerSocketChannel server = (ServerSocketChannel) key
- 59. .channel();
- 60. // 获得和客户端连接的通道
- 61. SocketChannel channel = server.accept();
- 62. // 设置成非阻塞
- 63. channel.configureBlocking(false);
- 64.
- 65. //在这里可以给客户端发送信息哦
- 66. channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
- 67. //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
- 68. channel.register(this.selector, SelectionKey.OP_READ);
- 69.
- 70. // 获得了可读的事件
- 71. } else if (key.isReadable()) {
- 72. read(key);
- 73. }
- 74.
- 75. }
- 76.
- 77. }
- 78. }
- 79. /**
- 80. * 处理读取客户端发来的信息 的事件
- 81. * @param key
- 82. * @throws IOException
- 83. */
- 84. public void read(SelectionKey key) throws IOException{
- 85. // 服务器可读取消息:得到事件发生的Socket通道
- 86. SocketChannel channel = (SocketChannel) key.channel();
- 87. // 创建读取的缓冲区
- 88. ByteBuffer buffer = ByteBuffer.allocate(10);
- 89. channel.read(buffer);
- 90. byte[] data = buffer.array();
- 91. String msg = new String(data).trim();
- 92. System.out.println("服务端收到信息:"+msg);
- 93. ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
- 94. channel.write(outBuffer);// 将消息回送给客户端
- 95. }
- 96.
- 97. /**
- 98. * 启动服务端测试
- 99. * @throws IOException
- 100. */
- 101. public static void main(String[] args) throws IOException {
- 102. NIOServer server = new NIOServer();
- 103. server.initServer(8000);
- 104. server.listen();
- 105. }
- 106.
- 107. }
客户端:
- 1. package cn.nio;
- 2.
- 3. import java.io.IOException;
- 4. import java.net.InetSocketAddress;
- 5. import java.nio.ByteBuffer;
- 6. import java.nio.channels.SelectionKey;
- 7. import java.nio.channels.Selector;
- 8. import java.nio.channels.SocketChannel;
- 9. import java.util.Iterator;
- 10.
- 11. /**
- 12. * NIO客户端
- 13. *
- 14. */
- 15. public class NIOClient {
- 16. //通道管理器
- 17. private Selector selector;
- 18.
- 19. /**
- 20. * 获得一个Socket通道,并对该通道做一些初始化的工作
- 21. * @param ip 连接的服务器的ip
- 22. * @param port 连接的服务器的端口号
- 23. * @throws IOException
- 24. */
- 25. public void initClient(String ip,int port) throws IOException {
- 26. // 获得一个Socket通道
- 27. SocketChannel channel = SocketChannel.open();
- 28. // 设置通道为非阻塞
- 29. channel.configureBlocking(false);
- 30. // 获得一个通道管理器
- 31. this.selector = Selector.open();
- 32.
- 33. // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
- 34. //用channel.finishConnect();才能完成连接
- 35. channel.connect(new InetSocketAddress(ip,port));
- 36. //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
- 37. channel.register(selector, SelectionKey.OP_CONNECT);
- 38. }
- 39.
- 40. /**
- 41. * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
- 42. * @throws IOException
- 43. */
- 44. @SuppressWarnings("unchecked")
- 45. public void listen() throws IOException {
- 46. // 轮询访问selector
- 47. while (true) {
- 48. selector.select();
- 49. // 获得selector中选中的项的迭代器
- 50. Iterator ite = this.selector.selectedKeys().iterator();
- 51. while (ite.hasNext()) {
- 52. SelectionKey key = (SelectionKey) ite.next();
- 53. // 删除已选的key,以防重复处理
- 54. ite.remove();
- 55. // 连接事件发生
- 56. if (key.isConnectable()) {
- 57. SocketChannel channel = (SocketChannel) key
- 58. .channel();
- 59. // 如果正在连接,则完成连接
- 60. if(channel.isConnectionPending()){
- 61. channel.finishConnect();
- 62.
- 63. }
- 64. // 设置成非阻塞
- 65. channel.configureBlocking(false);
- 66.
- 67. //在这里可以给服务端发送信息哦
- 68. channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
- 69. //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
- 70. channel.register(this.selector, SelectionKey.OP_READ);
- 71.
- 72. // 获得了可读的事件
- 73. } else if (key.isReadable()) {
- 74. read(key);
- 75. }
- 76.
- 77. }
- 78.
- 79. }
- 80. }
- 81. /**
- 82. * 处理读取服务端发来的信息 的事件
- 83. * @param key
- 84. * @throws IOException
- 85. */
- 86. public void read(SelectionKey key) throws IOException{
- 87. //和服务端的read方法一样
- 88. }
- 89.
- 90.
- 91. /**
- 92. * 启动客户端测试
- 93. * @throws IOException
- 94. */
- 95. public static void main(String[] args) throws IOException {
- 96. NIOClient client = new NIOClient();
- 97. client.initClient("localhost",8000);
- 98. client.listen();
- 99. }
- 100.
- 101. }
转载自:https://www.cnblogs.com/zedosu/p/6666984.html
IO、NIO、AIO理解的更多相关文章
- IO NIO AIO及常用框架概述
概述 nio 同步: 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写). 异步: 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需 ...
- 一文理解Java IO/NIO/AIO
目录 概述 一.IO流(同步.阻塞) 二.NIO(同步.非阻塞) 三.NIO2(异步.非阻塞) 正文 概述 在我们学习Java的IO流之前,我们都要了解几个关键词 同步与异步(synchronou ...
- Java之io nio aio 的区别
这个问题最近面试总是遇到,作为一个只会写流水代码的程序员,一脸懵逼.看了网上的解释,看的还是很模糊,说下我对这个的理解. 先引出一个话题,两个大水缸,一个空一个满,让你把一个缸里面的水弄到另一个里面. ...
- JAVA的 IO NIO AIO笔记
IO linux内核将所有外部设备都看做一个文件来操作,对一个文件的读写会调用内核系统命令,放回一个file descriptor(文件描述符), 对一个socket的读写也会有相应 ...
- 五种I/O 模式,select、epoll方法的理解,BIO、NIO、AIO理解 相关文章
一.io方式 Linux网络编程 五种I/O 模式及select.epoll方法的理解 web优化必须了解的原理之I/o的五种模型和web的三种工作模式 五种I/O 模式——阻塞(默认IO模式),非阻 ...
- IO回忆录之怎样过目不忘(BIO/NIO/AIO/Netty)
有热心的网友加我微信,时不时问我一些技术的或者学习技术的问题.有时候我回微信的时候都是半夜了.但是我很乐意解答他们的问题.因为这些年轻人都是很有上进心的,所以在我心里他们就是很优秀的,我愿意多和努力的 ...
- I/O模型系列之三:IO通信模型BIO NIO AIO
一.传统的BIO 网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请 ...
- IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞 区别参考
参考https://www.cnblogs.com/aspirant/p/6877350.html?utm_source=itdadao&utm_medium=referral IO复用,AI ...
- IO复用,AIO,BIO,NIO,同步,异步,阻塞和非阻塞 区别(百度)
如果面试问到IO操作,这篇文章提到的问题,基本是必问,百度的面试官问我三个问题 (1)什么是NIO(Non-blocked IO),AIO,BIO (2) java IO 与 NIO(New IO)的 ...
随机推荐
- Linux 集群架构
集群介绍 Keepalived 配置高可用集群
- Ansible Playbook 使用变量
如何在 Playbook 中定义并使用变量: vars: - user: "test" # 定义变量 tasks: - name: create user user: name=& ...
- HTML 引用
关于 HTML 引用: (1) <q> 和 <blockquote> 用于实现长短不一的引用语(2) <q> 用于短的引用,<blockquote> 用 ...
- 在RDLC报表中对纸张的设置
RDLC报表是存放成XML文件格式的,这一点你可以直接打开RDLC报表文件看一下,而且在使用时,通过ReportViewer来读取报表并与数据源进行合成,也就是说RDLC是定义了一个格式,那就不能通过 ...
- Qt编写带频谱的音乐播放器
之前有个项目需要将音频文件的频谱显示出来,想了很多办法,后面发现fmod这个好东西,还是跨平台的,就一个头文件+一个库文件就行,简单小巧功能强大,人家做的真牛逼.为了不卡住界面,采用了多线程处理. 可 ...
- 【SpringBoot整合Elasticsearch】SpringBoot整合ElasticSearch
一.Linux下安装ElasticSearch 1.检测是否安装了Elasticsearch ps aux |grep elasticsearch 2.安装JDK 3.下载Elasticsearch ...
- Makefile伪目标
https://www.zybuluo.com/lishuhuakai/note/210174 本节我们讨论一个Makefile中的一个重要的特殊目标:伪目标. 伪目标是这样一个目标:它不代表一个真正 ...
- Android studio 运行demo时一直卡在"Installing APKS"时的解决办法
现象 一 File --- Settings 二 看图操作
- sencha touch Container tpl 监听组件插件(2013-9-14)
将http://www.cnblogs.com/mlzs/p/3279162.html中的功能插件化 插件代码: /* *tpl模版加入按钮 *<div class="x-button ...
- [转]redhat7(centos7) not registered to Red Hat Subscription Management
[root@controller0 ~]# yum install ntp Loaded plugins: fastestmirror, product-id, search-disabled-rep ...