zoukankan      html  css  js  c++  java
  • Netty学习(2):IO模型之NIO初探

    NIO 概述

    前面说到 BIO 有着创建线程多,阻塞 CPU 等问题,因此为解决 BIO 的问题,NIO 作为同步非阻塞 IO模型,随 JDK1.4 而出生了。

    在前面我们反复说过4个概念:同步、异步、阻塞、非阻塞。因此,我们就首先用最简单的语言说一下他们的区别,这里,我们心里暂时有个概念即可,在后面的学习过程中,还会对其进行深入的探讨学习。

    概念对比

    首先,我们先要确立一个概念,就是一个IO操作其实是分为两步的,

    1. 发起IO请求,即准备数据和区域;
    2. 实际的IO操作。

    而区分一个 IO模型是同步还是异步,就取决于在进行第2个步骤时,即实际进行读写过程中,其是否会阻塞线程,如果会阻塞,那么就是同步的,如果不会(这里就需要操作系统内核来进行操作了),而是完成后,再通知操作线程,那么其就是异步的(AIO 就是 OS 来完成文件读写操作,在完成后通过消息机制来通过线程读写完毕)。

    紧接着,区分一个模型是阻塞还是非阻塞,而是看其在数据没有准备完毕时,即连接建立后,但还没有数据过来,请求是需要卡在这等待,还是可以去干其他的事。

    作为上文所述的 BIO 来说,首先因为数据是否读写完成,需要其一直在循环里判断,因此其是同步的,并且如果没有数据的话,其会一直卡在 read 操作上,不能继续往下执行,因此 BIO 是一种同步阻塞模型。

    而 NIO 的数据读写完成与否,也需要自己来判断,因此也是同步的,但其只有当发生真正读写时,才会进行操作,如果没有准备完毕的话,则可以去做其他的事,因此 NIO 是一个同步非阻塞 IO模型。

    总结一下,我们看一个模型是同步还是异步,其实看的是数据准备完毕后的消息通知机制;看是阻塞还是非阻塞的,则是看线程在请求后的线程状态。

    下面就让我们来看一下 NIO 的基本概念。

    NIO基本介绍

    1. 从 JDK1.4 后,官方引入的 IO 新特性,称为 NIO,是一个同步非阻塞模型;
    2. 相关类在 java.nio 包下,有 3大核心组件,Channel,Buffer,Selector
    3. NIO 是面向缓存区编程的,或者说可以是面向块的,而不是面向字节,因此极大提高了编程的灵活性;
    4. NIO 因为其核心组件和面向缓存区编程的特性,因此 数据总是从 Channel 读取到 Buffer 中,或者从Buffer 读取到 Channel 中,而 Selector 的作用就是监听多个 Channel,当 Channel 中有请求需要处理,就进行处理
    5. 。。。。。(官网或者搜一下,有很多,在这里就不多赘述了)

    组件介绍

    Buffer

    缓存区本质是一个可以读写程序的内存块,我们可以将其简单理解为一个容器。在其内部还提供了一些方法,方便编程人员对其进行操作。

    其所有可用子类如 ByteBuffer 等继承的父类为 Buffer,其有一些公有属性需要牢记。

    属性 说明
    mark 标记位,在调用 mark() 方法后,可以将当前 position 设为标记,后续在调用 reset() 方法时,就可以将 position 重置回该位置,如果不设置,则为负数,那么在调用 reset 时,会报错。(一般不需要设置)
    position 缓存区内将要被写或读的节点下标,该值不能为负数,且永远小于等于 limit
    limit 缓存区内第一个不能写或读的节点下标,该值不能为负数,且永远小于等于 capacity
    capacity 缓存区被创建时指定的大小,该值不能为负数,且无法更改
    mark <= position <= limit <= capacity
    

    Buffer 代码演示

     IntBuffer intBuffer = IntBuffer.allocate(5);
            for (int i = 1; i <= intBuffer.capacity(); i++) {
                intBuffer.put(i);
            }
            // 翻转,读写切换
            intBuffer.flip();
            while (intBuffer.hasRemaining()) {
                System.out.println(intBuffer.get());
            }
    
            intBuffer.position(0);
            for (int i = 1; i <= intBuffer.capacity(); i++) {
                intBuffer.put(i * i);
            }
            intBuffer.flip();
            while (intBuffer.hasRemaining()) {
                System.out.println(intBuffer.get());
            }
        }
    

    从上述代码中,可以看到 缓存区是一个可读可写的区域,且像数组容器一样可以在里面通过下标的方式进行移动,从而修改指定地方内容。

    Channel

    Channel 是流中的一个组件,在使用过程中,通过流来生成。

    Channel 是 NIO 中的一个接口,其实现类中,我们使用的比较多的有FileChannel,ServerSocketChannel,SocketChannelDatagramChannel,其中 FileChannel 用于文件的读写,ServerSocketChannel 和 SocketChannel 用于TCP数据读写,DatagramChannel 用于 UDP 数据读写。

    这里只描述一些 Channel 的一些简单概念,在后续文章:《文件操作》中,再用代码详细展示 Channel 的作用。

    Selector

    selector 解决的是 BIO 的线程阻塞和一个请求就需要创建一个线程的问题。selector 可以监控多个 Channel,如果对应的 Channel 有 Event 发生,就可以获取对应 Event,然后根据获取 Event 的不同去进行不同的处理逻辑,这样就不必每个请求都创建线程,并且只有当真正有读写事件发生时,才会进行操作。

    也是只描述 Selector 的一些简单概念,在后续文章:《网络编程》中,再用代码详细展示 Selector 的作用。

    总结

    在本文中,我们初步介绍了 NIO 的概念,以及其的 3个核心组件:Buffer,Channel 和 Selector。

    在后续的文章中,我们将对其分别进行介绍,通过 NIO 来逐步引入 Netty 的实现。

    本文中代码已上传到 GitHub 上,地址为 https://github.com/wb1069003157/nettyPre-research ,欢迎大家来讨论,探讨。

    iceWang公众号

    文章在公众号「iceWang」第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!

  • 相关阅读:
    专门针对初学者的Node.js教程
    windows版的node.js简单示例
    bzoj 1003: [ZJOI2006]物流运输【spfa+dp】
    bzoj 3573: [Hnoi2014]米特运输【树形dp+瞎搞】
    bzoj 1082: [SCOI2005]栅栏【二分+dfs】
    bzoj 2440: [中山市选2011]完全平方数【莫比乌斯函数+二分】
    bzoj 1049: [HAOI2006]数字序列【dp+二分+瞎搞】
    bzoj 2588: Spoj 10628. Count on a tree【主席树+倍增】
    bzoj 4551: [Tjoi2016&Heoi2016]树【并查集】
    bzoj 4310: 跳蚤【后缀数组+st表+二分+贪心】
  • 原文地址:https://www.cnblogs.com/JRookie/p/12335739.html
Copyright © 2011-2022 走看看