zoukankan      html  css  js  c++  java
  • NIO

    NIO

    标签(空格分隔): 异步非阻塞 输入输出流


    1. 传统的BIO

    BIO(Blocking I/O) 即同步并阻塞I/O, 在NIO(Non-Blocking)出现之前主要使用BIO以及新建线程的方式来解决并发请求, 但是这样很容易收到线程数量瓶颈的限制. 下面是典型的BIO编程模型.

    {
     ExecutorService executor = Excutors.newFixedThreadPollExecutor(100);//线程池
    
     ServerSocket serverSocket = new ServerSocket();
     serverSocket.bind(8088);
     while(!Thread.currentThread.isInturrupted()){//当前线程未中断
         Socket socket = serverSocket.accept();
         executor.submit(new ConnectIOnHandler(socket));//为新的连接创建新的线程
    }
    
    class ConnectIOnHandler extends Thread{
        private Socket socket;
        public ConnectIOnHandler(Socket socket){
           this.socket = socket;
        }
        public void run(){
          while(!Thread.currentThread.isInturrupted()&&!socket.isClosed()){死循环处理读写事件
              String someThing = socket.read()....//读取数据
              if(someThing!=null){
                 ......//处理数据
                 socket.write()....//写数据
              }
          }
        }
    }
    

    之所以使用多线程是因为accept, read, write三个函数都是同步阻塞的, 当一个链接存在的时候, 系统是阻塞的, 所以利用多线程让CPU处理更多的申请, 多线程的本质:

    1. 利用CPU的多核特性.
    2. 当I/O阻塞系统, 但CPU空闲的时候, 可以利用多线程使用CPU资源.
    3. 现在的多线程一般都是使用线程池, 可以让线程的创建和回收成本相对较低, 在活动连接数不是特别高的情况下(单机小于1000), 这种模型是比较不错的, 可以让每一个链接专注于自己的I/O并且编程模型相对简单, 也不用考虑系统的过载,限流等问题. 但是当处理百万级别的链接的时候, 使用这种模型肯定是不切实际的, 让CPU去创建这么多的线程是不可能的.

    2. 优秀的NIO

    从JDK1.4开始, Java提供了一系列用于改进的输入/输出处理的新特性, 被统称为NIO. 新增了许多用于处理输入输出的类, 这些类都被放在java.nio包及其子包之下, 并且对原java.io包中的很多类进行改写, 满足了NIO的功能. NIO采用内存映射文件的方式来处理输入输出, NIO将文件或者文件的一段区域映射到内存中, 这样就可以像访问内存一样访问文件了, NIO与原来的IO有着同样的作用和目的, 但是使用的方法是完全不同的, NIO支持面向缓冲区(Buffer)的基于通道(Channel)的IO操作, NIO将会以更加高效的方式进行文件的读写操作.

    2.1 缓冲区Buffer

    缓冲区有直接缓冲区和非直接缓冲区之分, 它实际上也是一段内存空间,在NIO库中, 所有的数据都是用缓冲区处理的, 在读取数据的时候, 它是直接读到缓冲区中的, 在写入数据的时候, 他也是直接写入到缓冲区中的, 流程如下图:

    2.2 通道Channel

    Channel 表示到实体如 硬件设备, 文件,网络套接字或可以执行一个或多个不同I/O操作的程序组件开放的链接.
    Channel和传统IO的Stream很相似, 主要的区别为: 通道是双向的, 通过一个Channel即可以读, 也可以写. 而Stream只能进行单向操作. 通道是一个对象, 通过它可以读取和写入数据, 当然所有的数据都通过Buffer对象来处理. 我们不会将字节直接写入通道之中, 相反是将数据写入包含一个或者多个字节的缓冲区. 同样不会直接从通道中读取字节, 而是将数据从通道读入缓冲区, 再从缓冲区获取这个字节.

    2.3 选择器Selector

    Selector类是NIO的核心类, Selector选择器提供了选择已经就绪的任务的能力. Selector会不断轮询注册在上面所有的Channel, 如果某个Channel为读写等时间做好了准备, 那么久处于就绪状态, 通过Selector 可以不断的轮询发现出就绪的Channel, 进行后续的IO操作.

    一个Selector可以同时轮询多个Channel. 这样, 一个单独的线程就可以管理多个Channel,从而管理多个网络连接. 这样就不用为每一个链接都创建一个线程, 同时也避免了多线程之间上下文切换带来的额外开销.

    与Selector有关的一个管件类是SelectionKey, 一个SelectionKey表示一个到达的事件, 这两个类构成了服务端处理业务的关键逻辑.

  • 相关阅读:
    《Selenium自动化测试实战》新书上市,有需要朋友们可以了解下,欢迎大家多提宝贵意见
    OrchardCore 如何动态加载模块?
    性能测试基础知识体系
    职场新人如何快速融入团队
    技术之外的工程师另类成长指南
    4.17-线上-技术沙龙问题汇总答疑
    3.20-上海-技术沙龙问题汇总答疑
    推荐书单4.0:测试工程师破局之路
    从技术专家到技术管理,我对管理的思考
    chrome打开本地链接
  • 原文地址:https://www.cnblogs.com/A-FM/p/12673857.html
Copyright © 2011-2022 走看看