参考 https://www.cnblogs.com/zedosu/p/6666984.html

摘要: 关于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方式适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。

附录:下面附上一个别人写的java NIO的例子。 
服务端:

  1. 1. package cn.nio;
  2. 2.
  3. 3. import java.io.IOException;
  4. 4. import java.net.InetSocketAddress;
  5. 5. import java.nio.ByteBuffer;
  6. 6. import java.nio.channels.SelectionKey;
  7. 7. import java.nio.channels.Selector;
  8. 8. import java.nio.channels.ServerSocketChannel;
  9. 9. import java.nio.channels.SocketChannel;
  10. 10. import java.util.Iterator;
  11. 11.
  12. 12. /**
  13. 13. * NIO服务端
  14. 14. *
  15. 15. */
  16. 16. public class NIOServer {
  17. 17.    //通道管理器
  18. 18.    private Selector selector;
  19. 19.
  20. 20.    /**
  21. 21.     * 获得一个ServerSocket通道,并对该通道做一些初始化的工作
  22. 22.     * @param port  绑定的端口号
  23. 23.     * @throws IOException
  24. 24.     */
  25. 25.    public void initServer(int port) throws IOException {
  26. 26.        // 获得一个ServerSocket通道
  27. 27.        ServerSocketChannel serverChannel = ServerSocketChannel.open();
  28. 28.        // 设置通道为非阻塞
  29. 29.        serverChannel.configureBlocking(false);
  30. 30.        // 将该通道对应的ServerSocket绑定到port端口
  31. 31.        serverChannel.socket().bind(new InetSocketAddress(port));
  32. 32.        // 获得一个通道管理器
  33. 33.        this.selector = Selector.open();
  34. 34.        //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
  35. 35.        //当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
  36. 36.        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
  37. 37.    }
  38. 38.
  39. 39.    /**
  40. 40.     * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
  41. 41.     * @throws IOException
  42. 42.     */
  43. 43.    @SuppressWarnings("unchecked")
  44. 44.    public void listen() throws IOException {
  45. 45.        System.out.println("服务端启动成功!");
  46. 46.        // 轮询访问selector
  47. 47.        while (true) {
  48. 48.            //当注册的事件到达时,方法返回;否则,该方法会一直阻塞
  49. 49.            selector.select();
  50. 50.            // 获得selector中选中的项的迭代器,选中的项为注册的事件
  51. 51.            Iterator ite = this.selector.selectedKeys().iterator();
  52. 52.            while (ite.hasNext()) {
  53. 53.                SelectionKey key = (SelectionKey) ite.next();
  54. 54.                // 删除已选的key,以防重复处理
  55. 55.                ite.remove();
  56. 56.                // 客户端请求连接事件
  57. 57.                if (key.isAcceptable()) {
  58. 58.                    ServerSocketChannel server = (ServerSocketChannel) key
  59. 59.                            .channel();
  60. 60.                    // 获得和客户端连接的通道
  61. 61.                    SocketChannel channel = server.accept();
  62. 62.                    // 设置成非阻塞
  63. 63.                    channel.configureBlocking(false);
  64. 64.
  65. 65.                    //在这里可以给客户端发送信息哦
  66. 66.                    channel.write(ByteBuffer.wrap(new String("向客户端发送了一条信息").getBytes()));
  67. 67.                    //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
  68. 68.                    channel.register(this.selector, SelectionKey.OP_READ);
  69. 69.
  70. 70.                    // 获得了可读的事件
  71. 71.                } else if (key.isReadable()) {
  72. 72.                        read(key);
  73. 73.                }
  74. 74.
  75. 75.            }
  76. 76.
  77. 77.        }
  78. 78.    }
  79. 79.    /**
  80. 80.     * 处理读取客户端发来的信息 的事件
  81. 81.     * @param key
  82. 82.     * @throws IOException
  83. 83.     */
  84. 84.    public void read(SelectionKey key) throws IOException{
  85. 85.        // 服务器可读取消息:得到事件发生的Socket通道
  86. 86.        SocketChannel channel = (SocketChannel) key.channel();
  87. 87.        // 创建读取的缓冲区
  88. 88.        ByteBuffer buffer = ByteBuffer.allocate(10);
  89. 89.        channel.read(buffer);
  90. 90.        byte[] data = buffer.array();
  91. 91.        String msg = new String(data).trim();
  92. 92.        System.out.println("服务端收到信息:"+msg);
  93. 93.        ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
  94. 94.        channel.write(outBuffer);// 将消息回送给客户端
  95. 95.    }
  96. 96.
  97. 97.    /**
  98. 98.     * 启动服务端测试
  99. 99.     * @throws IOException
  100. 100.     */
  101. 101.    public static void main(String[] args) throws IOException {
  102. 102.        NIOServer server = new NIOServer();
  103. 103.        server.initServer(8000);
  104. 104.        server.listen();
  105. 105.    }
  106. 106.
  107. 107. }

客户端:

  1. 1. package cn.nio;
  2. 2.
  3. 3. import java.io.IOException;
  4. 4. import java.net.InetSocketAddress;
  5. 5. import java.nio.ByteBuffer;
  6. 6. import java.nio.channels.SelectionKey;
  7. 7. import java.nio.channels.Selector;
  8. 8. import java.nio.channels.SocketChannel;
  9. 9. import java.util.Iterator;
  10. 10.
  11. 11. /**
  12. 12. * NIO客户端
  13. 13. *
  14. 14. */
  15. 15. public class NIOClient {
  16. 16.    //通道管理器
  17. 17.    private Selector selector;
  18. 18.
  19. 19.    /**
  20. 20.     * 获得一个Socket通道,并对该通道做一些初始化的工作
  21. 21.     * @param ip 连接的服务器的ip
  22. 22.     * @param port  连接的服务器的端口号
  23. 23.     * @throws IOException
  24. 24.     */
  25. 25.    public void initClient(String ip,int port) throws IOException {
  26. 26.        // 获得一个Socket通道
  27. 27.        SocketChannel channel = SocketChannel.open();
  28. 28.        // 设置通道为非阻塞
  29. 29.        channel.configureBlocking(false);
  30. 30.        // 获得一个通道管理器
  31. 31.        this.selector = Selector.open();
  32. 32.
  33. 33.        // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调
  34. 34.        //用channel.finishConnect();才能完成连接
  35. 35.        channel.connect(new InetSocketAddress(ip,port));
  36. 36.        //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
  37. 37.        channel.register(selector, SelectionKey.OP_CONNECT);
  38. 38.    }
  39. 39.
  40. 40.    /**
  41. 41.     * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理
  42. 42.     * @throws IOException
  43. 43.     */
  44. 44.    @SuppressWarnings("unchecked")
  45. 45.    public void listen() throws IOException {
  46. 46.        // 轮询访问selector
  47. 47.        while (true) {
  48. 48.            selector.select();
  49. 49.            // 获得selector中选中的项的迭代器
  50. 50.            Iterator ite = this.selector.selectedKeys().iterator();
  51. 51.            while (ite.hasNext()) {
  52. 52.                SelectionKey key = (SelectionKey) ite.next();
  53. 53.                // 删除已选的key,以防重复处理
  54. 54.                ite.remove();
  55. 55.                // 连接事件发生
  56. 56.                if (key.isConnectable()) {
  57. 57.                    SocketChannel channel = (SocketChannel) key
  58. 58.                            .channel();
  59. 59.                    // 如果正在连接,则完成连接
  60. 60.                    if(channel.isConnectionPending()){
  61. 61.                        channel.finishConnect();
  62. 62.
  63. 63.                    }
  64. 64.                    // 设置成非阻塞
  65. 65.                    channel.configureBlocking(false);
  66. 66.
  67. 67.                    //在这里可以给服务端发送信息哦
  68. 68.                    channel.write(ByteBuffer.wrap(new String("向服务端发送了一条信息").getBytes()));
  69. 69.                    //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
  70. 70.                    channel.register(this.selector, SelectionKey.OP_READ);
  71. 71.
  72. 72.                    // 获得了可读的事件
  73. 73.                } else if (key.isReadable()) {
  74. 74.                        read(key);
  75. 75.                }
  76. 76.
  77. 77.            }
  78. 78.
  79. 79.        }
  80. 80.    }
  81. 81.    /**
  82. 82.     * 处理读取服务端发来的信息 的事件
  83. 83.     * @param key
  84. 84.     * @throws IOException
  85. 85.     */
  86. 86.    public void read(SelectionKey key) throws IOException{
  87. 87.        //和服务端的read方法一样
  88. 88.    }
  89. 89.
  90. 90.
  91. 91.    /**
  92. 92.     * 启动客户端测试
  93. 93.     * @throws IOException
  94. 94.     */
  95. 95.    public static void main(String[] args) throws IOException {
  96. 96.        NIOClient client = new NIOClient();
  97. 97.        client.initClient("localhost",8000);
  98. 98.        client.listen();
  99. 99.    }
  100. 100.
  101. 101. }

http://blog.csdn.net/jiyiqinlovexx/article/details/42619097 

java BIO NIO IO的更多相关文章

  1. Java BIO NIO 与 AIO

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

  2. JAVA bio nio aio

    [转自]http://qindongliang.iteye.com/blog/2018539 在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? ...

  3. java BIO/NIO/AIO 学习

    一.了解Unix网络编程5种I/O模型 1.1.阻塞式I/O模型 阻塞I/O(blocking I/O)模型,进程调用recvfrom,其系统调用直到数据报到达且被拷贝到应用进程的缓冲区中或者发生错误 ...

  4. java BIO/NIO

    一.BIO Blocking IO(即阻塞IO); 1.      特点: a)   Socket服务端在监听过程中每次accept到一个客户端的Socket连接,就要处理这个请求,而此时其他连接过来 ...

  5. 3. 彤哥说netty系列之Java BIO NIO AIO进化史

    你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...

  6. AIO,BIO,NIO,IO复用,同步,异步,阻塞和非阻塞

    (1)什么是NIO(Non-blocked IO),AIO,BIO (2) 区别 (3)select 与 epoll,poll区别 1.什么是socket?什么是I/O操作? 什么是socket? 实 ...

  7. JAVA BIO,NIO,Reactor模式总结

    传统同步阻塞I/O(BIO) 在NIO之前编写服务器使用的是同步阻塞I/O(Blocking I/O).下面是一个典型的线程池客服端服务器示例代码,这段代码在连接数急剧上升的情况下,这个服务器代码就会 ...

  8. I/O模型系列之三:IO通信模型BIO NIO AIO

    一.传统的BIO 网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请 ...

  9. Java BIO、NIO、AIO

    同步与异步 同步与异步的概念, 关注的是 消息通信机制 同步是指发出一个请求, 在没有得到结果之前该请求就不返回结果, 请求返回时, 也就得到结果了. 比如洗衣服, 把衣服放在洗衣机里, 没有洗好之前 ...

随机推荐

  1. linux修改系统时间和时区

    1.修改系统时间linux系统时钟有两个,一个是硬件时钟,即BIOS时间,就是我们进行CMOS设置时看到的时间,另一个是系统时钟,是linux系统Kernel时间.当Linux启动时,系统Kernel ...

  2. [LeetCode] 275. H-Index II H指数 II

    Follow up for H-Index: What if the citations array is sorted in ascending order? Could you optimize ...

  3. QT源码分析:QTcpServer

    最近在看有关IO复用方面的内容,自己也用标准c++库实现了select模型.iocp模型.poll模型.回过头来很想了解QT的socket是基于什么模型来实现的,所以看了QT关于TcpServer实现 ...

  4. MAX10 ADC的一些基知识

    MAX10 ADC 的一些知识 1.        MAX 10 内部集成的12bit SAR ADC的特点为: a.        采样速率高达1Mhz. b.        模拟通道多达18个,单 ...

  5. Postman系列四:Postman接口请求设置环境变量和全局变量、测试沙箱和测试断言、测试集运行与导入数据文件

    一:Postman中接口请求设置环境变量和全局变量 全局变量和环境变量可以通过Pre-request Script和Tests设置,会在下面测试沙箱和测试断言中讲到. 全局变量的设置:官网参考http ...

  6. 【知识总结】动态 DP

    勾起了我悲伤的回忆 -- NOIP2018 316pts -- 主要思想:将 DP 过程分解为方便单点修改和一个区间合并的操作(通常类似矩阵乘法),然后用数据结构(通常为线段树)维护. 例:给定一个长 ...

  7. PostgreSql11.5源码安装

    参考:https://yq.aliyun.com/articles/675687/ 1.先下载postgresql11.5的源码包:https://www.postgresql.org/ftp/sou ...

  8. [IOT] - Raspberry Pi 3B + Windows 10 IOT Core + .Net Core Web 部署

    硬件:Raspberry Pi 3B 系统:Windows 10 IOT Core 应用:.Net Core Web 部署流程 1. 系统安装 1.1 下载并安装 Windows 10 IoT Cor ...

  9. 2.RabbitMQ 的可靠性消息的发送

      本篇包含 1. RabbitMQ 的可靠性消息的发送 2. RabbitMQ 集群的原理与高可用架构的搭建 3. RabbitMQ 的实践经验   上篇包含 1.MQ 的本质,MQ 的作用 2.R ...

  10. kubernetes 实践四:Pod详解

    本篇是关于k8s的Pod,主要包括Pod和容器的使用.Pod的控制和调度管理.应用配置管理等内容. Pod的定义 Pod是k8s的核心概念一直,就名字一样,是k8s中一个逻辑概念.Pod是docekr ...