Java NIO中的缓冲区Buffer(一)缓冲区基础

什么是缓冲区(Buffer)

定义

简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer、ByteBuffer、DoubleBuffer等的源码),Buffer类内部其实就是一个基本数据类型的数组,以及对这个缓冲数组的各种操作;

常见的缓冲区如ByteBuffer、IntBuffer、DoubleBuffer...内部对应的数组依次是byte、int、double...

与通道的关系

在Java NIO中,缓冲区主要是跟通道(Channel)打交道,数据总是从缓冲区写入到通道中,或者从通道读取数据到缓冲区;

继承结构

关于Buffer的继承结构,我们可以简单的以ByteBuffer为例,如下:

Buffer是顶层抽象类,ByteBuffer继承Buffer,也是抽象类,ByteBuffer最常见的两个具体实现类如下:

DirectByteBuffer(JVM堆外部、通过unsafe.allocateMemory实现)、HeapByteBuffer(JVM堆)

缓冲区的四个属性(capacity、limit、position、mark)

容量(capacity)

capacity指的是缓冲区能够容纳元素的最大数量,这个值在缓冲区创建时被设定,而且不能够改变,如下,我们创建了一个最大容量为10的字节缓冲区;

ByteBuffer bf = ByteBuffer.allocate(10);

上界(limit)

limit指的是缓冲区中第一个不能读写的元素的数组下标索引,也可以认为是缓冲区中实际元素的数量;

位置(position)

position指的是下一个要被读写的元素的数组下标索引,该值会随get()和put()的调用自动更新;

标记(mark)

一个备忘位置,调用mark()方法的话,mark值将存储当前position的值,等下次调用reset()方法时,会设定position的值为之前的标记值;

四个属性值之间的关系

根据以上四个属性的定义,我们可以总结出它们之间的关系如下:

0 <= mark <= position <= limit <= capacity

举个例子,观察四个属性值的变化

 1、创建一个容量大小为10的字符缓冲区

ByteBuffer bf = ByteBuffer.allocate(10);

此时:mark = -1; position = 0; limit = 10; capacity = 10;

2、往缓冲区中put()五个字节

bf.put((byte)‘H‘).put((byte)‘e‘).put((byte)‘l‘).put((byte)‘l‘).put((byte)‘0‘);

注意这里一个字符是占用两个字节的,但是英文字符只占用一个字节,所以这样是可以实现储存效果的;

此时:mark = -1; position = 5; limit = 10; capacity = 10;

3、调用flip()方法,切换为读就绪状态

bf.flip();

此时:mark = -1; position = 0; limit = 5; capacity = 10;

 4、读取两个元素

System.out.println("" + (char) bf.get() + (char) bf.get());

此时:mark = -1; position = 2; limit = 5; capacity = 10;

5、标记此时的position位置

bf.mark();

此时:mark = 2; position = 2; limit = 5; capacity = 10;

6、读取两个元素后,恢复到之前mark的位置处

System.out.println("" + (char) bf.get() + (char) bf.get());
bf.reset();

属性变化情况:

执行完第一行代码:mark = 2; position = 4; limit = 5; capacity = 10;

执行完第二行代码:mark = 2; position = 2; limit = 5; capacity = 10;

7、调用compact()方法,释放已读数据的空间,准备重新填充缓存区

bf.compact();

此时:mark = 2; position = 3; limit = 10; capacity = 10;

注意观察数组中元素的变化,实际上进行了数组拷贝,抛弃了已读字节元素,保留了未读字节元素;

缓冲区比较

其实查看equals源码就可以知道是如何比较的,如下(以ByteBuffer为例):

public boolean equals(Object ob) {
        if (this == ob)
            return true;
        if (!(ob instanceof ByteBuffer))
            return false;
        ByteBuffer that = (ByteBuffer)ob;
        if (this.remaining() != that.remaining())
            return false;
        int p = this.position();
        for (int i = this.limit() - 1, j = that.limit() - 1; i >= p; i--, j--)
            if (!equals(this.get(i), that.get(j)))
                return false;
        return true;
    }

总的来说,两个缓冲区被认为相等的条件如下(以下内容直接摘自《Java NIO》):

  1. 两个对象类型相同。包含不同数据类型的 buffer 永远不会相等,而且 buffer绝不会等于非 buffer 对象。
  2. 两个对象都剩余同样数量的元素。Buffer 的容量不需要相同,而且缓冲区中剩余数据的索引也不必相同。但每个缓冲区中剩余元素的数目(从位置到上界)必须相同。
  3. 在每个缓冲区中应被 Get()方法返回的剩余数据元素序列必须一致。

批量读写缓冲区数据

以ByteBuffer为例,使用如下API即可:

public ByteBuffer get(byte[] dst, int offset, int length)

public ByteBuffer put(byte[] src, int offset, int length)

public ByteBuffer get(byte[] dst)

public final ByteBuffer put(byte[] src)

实际上,后面两种方法内部就是调用前面两种方法的;

参数的含义直接查看源码注释即可,写的很清楚,如put(byte[] src, int offset, int length)方法的注释:

    /* @param  src
     *         The array from which bytes are to be read
     *
     * @param  offset
     *         The offset within the array of the first byte to be read;
     *         must be non-negative and no larger than <tt>array.length</tt>
     *
     * @param  length
     *         The number of bytes to be read from the given array;
     *         must be non-negative and no larger than
     *         <tt>array.length - offset</tt>
     */

参考资料

《Java NIO》

时间: 2024-10-26 03:19:39

Java NIO中的缓冲区Buffer(一)缓冲区基础的相关文章

java NIO中的buffer和channel

缓冲区(Buffer):一,在 Java NIO 中负责数据的存取.缓冲区就是数组.用于存储不同数据类型的数据 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:ByteBufferCharBufferShortBufferIntBufferLongBufferFloatBufferDoubleBuffer 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区 二.缓冲区存取数据的两个核心方法:put() : 存入数据到缓冲区中get() : 获取缓冲区中的数据

Java NIO中的Buffer 详解

Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的.缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存. Buffer的基本用法使用Buffer读写数据一般遵循以下四个步骤: 写入数据到Buffer    调用flip()方法    从Buffer中读取数据    调用clear()方法或者compact()方法 当向buffer写入数据时,

Java NIO(1)----Channel 和 Buffer

Java NIO 由以下几个核心部分组成: Channels Buffers Selectors 虽然Java NIO 中除此之外还有很多类和组件,但在我看来,Channel,Buffer 和 Selector 构成了核心的API.其它组件,如Pipe和FileLock,只不过是与三个核心组件共同使用的工具类.因此,在概述中我将集中在这三个组件上.其它组件会在单独的章节中讲到. Channel 和 Buffer 基本上,所有的 IO 在NIO 中都从一个Channel 开始.Channel 有点

Java NIO中的Glob模式详解

Java NIO中的Glob模式详解 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs 一.什么是Glob? 在编程设计中,Glob是一种模式,它使用通配符来指定文件名.例如:.java就是一个简单的Glob,它指定了所有扩展名为"java"的文件.Glob模式中广泛使用了两个通配符""和"?".其中星号表示"任意的字符或字符组成字符串",而问号则表示"任意单个字符&quo

Java nio 笔记:系统IO、缓冲区、流IO、socket通道

一.Java IO 和 系统 IO 不匹配 在大多数情况下,Java 应用程序并非真的受着 I/O 的束缚.操作系统并非不能快速传送数据,让 Java 有事可做:相反,是 JVM 自身在 I/O 方面效率欠佳.操作系统与 Java 基于流的 I/O模型有些不匹配.操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取(DMA)的协助下完成的.而 JVM 的 I/O 操作类喜欢操作小块数据--单个字节.几行文本.结果,操作系统送来整缓冲区的数据,java.io 包的流数据类再花大量时间

Java NIO中的通道Channel(二)分散/聚集 Scatter/Gather

什么是Scatter/Gather scatter/gather指的在多个缓冲区上实现一个简单的I/O操作,比如从通道中读取数据到多个缓冲区,或从多个缓冲区中写入数据到通道: scatter(分散):指的是从通道中读取数据分散到多个缓冲区Buffer的过程,该过程会将每个缓存区填满,直至通道中无数据或缓冲区没有空间: gather(聚集):指的是将多个缓冲区Buffer聚集起来写入到通道的过程,该过程类似于将多个缓冲区的内容连接起来写入通道: scatter/gather接口 如下是Scatte

java NIO中的Reactor相关知识汇总 (转)

一.引子 nio是java的IO框架里边十分重要的一部分内容,其最核心的就是提供了非阻塞IO的处理方式,最典型的应用场景就是处理网络连接.很多同学提起nio都能说起一二,但是细究其背后的原理.思想往往就开始背书,说来说去都是那么几句,其中不少人并不见的真的很理解.本人之前就属于此类,看了很多书和博客,但是大多数都只是讲了三件套和怎么使用,很少会很细致的讲背后的思想,那本次我们就来扒一扒吧.     很多博客描述nio都是这么说的:基于Reactor模式实现的多路非阻塞高性能的网络IO.那么我们就

JAVA NIO 中的 zerocopy 技术提高IO性能

参考: https://www.ibm.com/developerworks/linux/library/j-zerocopy/ 这篇文章介绍了 zerocopy技术来提高Linux平台上的IO密集型的JAVA应用程序的性能. zerocopy技术能够避免中间缓冲区中的冗余数据复制以及减少Linux内核空间和用户空间上下文交换的次数. 适用场景:Many Web applications serve a significant amount of static content, which am

JAVA NIO中selectedKeys返回的键集,对其中的SelectionKey执行操作之后,是否需要在selectedKeys()中对其执行remove 操作

今天一个东西需要用到java nio的东西.在网上查了一下资料,发现有Apache的Mina,Netty等,感觉JDK中带的NIO有点鸡肋啊.之前看过这部分的内容,但好长一段时间没有用,也就忘得七七八八了.如今是温故而知新,但其中遇到了些疑问: 先贴上代码吧: public static void main(String[] args) throws Exception{ Thread sh=new Thread(new Runnable() { public void run(){ try{