zoukankan      html  css  js  c++  java
  • NIO(二、Buffer)

    目录

    NIO(一、概述)
    NIO(二、Buffer)
    NIO(三、Channel)
    NIO(四、Selector)

    Buffer

    前文讲了NIO与IO的区别,那么这一章开始讲述NIO下核心类 - Buffer类

      上一章就说过,NIO的核心包括三个部分:通道(Channel)、选择器(Selector)、缓冲区(Buffer),尽管还有其它的部分,例如管道(Pipe)、文件锁(FileLock)、字符集(Charset)或甚是java.nio包下的异常类,这些都是作为工具而被使用。

      说起缓冲区,我们都知道是临时数据存储的地方,官方给它的定义是:

    A container for data of a specific primitive type. 特定基础类型数据的容器

      其实我们看它的实现类就能看出来

    • ByteBuffer
    • CharBuffer
    • DoubleBuffer
    • FloatBuffer
    • IntBuffer
    • LongBuffer
    • ShortBuffer

      几乎每种基础数据类型都会有一种Buffer的实现,它们的操作方式几乎一致,都有读(get)/写(put)操作,而且都是抽象类,所以,基于这些基础类型的缓冲区,还有更具体的实现,例如ByteBuffer类有HeapByteBuffer和MappedByteBuffer两个子类。第一次阅读代码可能有些奇怪,既然所有基础类型数据的缓冲区都从Buffer类继承,那么Buffer类中并没有抽象出读(get)/写(put)方法,而是每个子类中分别抽象本身类型的读(get)/写(put)操作。同样分配缓冲区大小方法( allocate() )也是在子类中定义,Buffer类本身并没对外提供任何初始化的方法,即便是构造也是为了子类继承使用,由此可见,在使用Buffer的时候,我们应该明确知道该使用哪一类基础类型的缓冲区。

    Capacity、Limit、Position

      我们阅读Buffer类代码会发现,Buffer类定义四个int类型的私有字段:mark、position、limit、capacity。其实了解完这四个字段,我们就明白Buffer是如何工作的。

    无论如何,它们的大小都会遵照这个规则 :mark <= position <= limit <= capacity

      这是Buffer读/写模式时position、limit、capacity的图。

      我们看一段简单代码:

    //code 
    IntBuffer intBuffer = IntBuffer.wrap(new int[]{1, 2, 3, 4, 5, 6, 7});
    System.out.println("capacity -> " + intBuffer.capacity());
    System.out.println("limit -> " + intBuffer.limit());
    System.out.println("position -> " + intBuffer.position());
    //切换读模式
    intBuffer.flip();
    System.out.println("capacity -> " + intBuffer.capacity());
    System.out.println("limit -> " + intBuffer.limit());
    System.out.println("position -> " + intBuffer.position());
    //console:
    //写模式
    capacity -> 7
    limit -> 7
    position -> 0
    //读模式
    capacity -> 7
    limit -> 0
    position -> 0
    

      初始化了一个IntBuffer对象,然后打印capacity、limit、position的值,清楚的可以看到,capacity和limit初始值是初始化容量的大小,position为0。当切换读模式时,capacity的值同样为7,limit和position值为0。
      接上一段代码:

    //code
    intBuffer.clear();
    intBuffer.put(100);
    intBuffer.put(200);
    System.out.println("position -> " + intBuffer.position());
    //切换读模式
    intBuffer.flip();
    int pVal0 = intBuffer.get(0);
    System.out.println("capacity -> " + intBuffer.capacity());
    System.out.println("limit -> " + intBuffer.limit());
    System.out.println("position -> " + intBuffer.position());
    //console
    position -> 2
    //读模式
    capacity -> 7
    limit -> 2
    position -> 0
    

      position就是你在往Buffer中写数据时,标示当前的位置,初始化的position肯定是0,当你写入一个数据,position就会增加1。但我们发现我们读操作并不会改变position的值,因为get操作只会检查索引有无越界,然后取出数据,并不像put操作会把position增加1。
      另外,要说明一下的是,mark也是Buffer中的一个标记,当缓冲区初始化时、设置新的position或limit的值时、清理缓冲区时、设置读模式时等,mark值都会被标为-1。仅仅在使用mark()方法的时候,mark字段才会被赋予position值。mark作为position的一个临时存储的变量而存在,我们随时都可以调用reset()把之前存储的原position值重新赋给position变量。当一个position不够用时,我们需要多一个临时变量存储,这似乎就是mark存在的意义了。

      同样,我们也发现capactiy就是缓冲区的容量,缓冲区的容量在初始化之后就不会变,无论你执行clear()或是compact()方法,它们都不会改变缓冲区的容量,除非被回收整个缓冲区。当我们初始化一个缓冲区之后,在明知道容量只有3的情况下,却硬是塞4个值,那么运行会抛出java.nio.BufferOverflowException异常。
      接上一段代码:

    System.out.println("limit -> " + intBuffer.limit());
    //code 1.1
    intBuffer.get(5);
    System.out.println("limit -> " + intBuffer.limit());
    //code 1.2
    intBuffer.limit(intBuffer.capacity());
    intBuffer.get(5);
    System.out.println("limit -> " + intBuffer.limit());
    
    //console
    limit -> 2
    //code 1.1
    java.lang.IndexOutOfBoundsException
    //code 1.2
    limit -> 7
    

      limit同样标示使用的上限,表示你能写多少数据,或你能读多少数据的值。换句话说,就是读/写的时候,limit大小决定了你能读/写多少。上述代码说明了这一点,进入代码块时limit的值为2,意味着只能读索引为0或1的数据,只有当limit被重新赋值,你才能读取新值范围以内的数据。

    线程安全

      Buffer类的实现,决定了这必定是线程不安全的,如果需要在多个线程中使用,我们需要主动加上同步控制。

    方法

      这里只介绍几个比较重要的操作方法。
      flip(),切换读模式。

    public final Buffer flip() {
            limit = position;
            position = 0;
            mark = -1;
            return this;
    }
    

      rewind(),重读,可以理解成倒带,即便已读过的数据内容也可重读一遍。

    public final Buffer rewind() {
            position = 0;
            mark = -1;
            return this;
    }
    

      clear(),清除缓冲区内容,可以将新的数据重新写进缓冲区.。

    public final Buffer clear() {
            position = 0;
            limit = capacity;
            mark = -1;
            return this;
    }
    
  • 相关阅读:
    100-days: twenty-four
    100-days: twenty-three
    100-days: twenty-two
    100-days: twenty-one
    100-days: twenty
    [NOI 2016]循环之美
    [NOI 2015]寿司晚宴
    [BZOJ 2655]calc
    [Codeforces 888G]Xor-MST
    [BZOJ 2839]集合计数
  • 原文地址:https://www.cnblogs.com/sally-zhou/p/6501623.html
Copyright © 2011-2022 走看看