Netty学习(2):IO模型之NIO初探

2019/04/10 10:10
阅读数 21

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」第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!

原文出处:https://www.cnblogs.com/JRookie/p/12335739.html

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部