JAVA NIO核心之缓冲区(Buffer)

NIO核心一:缓冲区(Buffer)

缓冲区(Buffer)

一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区 都是 Buffer 抽象类的子类.。Java NIO 中的 Buffer 主要用于与 NIO 通道进行 交互,数据是从通道读入缓冲区,从缓冲区写入通道中的。

JAVA NIO核心之缓冲区(Buffer)

Buffer 类及其子类

Buffer 就像一个数组,可以保存多个相同类型的数据。根 据数据类型不同 ,有以下 Buffer 常用子类:

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

上述 Buffer 类 他们都采用相似的方法进行管理数据,只是各自 管理的数据类型不同而已。都是通过如下方法获取一个 Buffer 对象:

static XxxBuffer allocate(int capacity) : 创建一个容量为capacity 的 XxxBuffer 对象

缓冲区的基本属性

Buffer 中的重要概念:

  • 容量 (capacity) :作为一个内存块,Buffer具有一定的固定大小,也称为”容量”,缓冲区容量不能为负,并且创建后不能更改。

  • 位置 (position) :下一个要读取或写入的数据的索引。缓冲区的位置不能为 负,并且不能大于其限制

  • 限制 (limit) :表示缓冲区中可以操作数据的大小(limit 后数据不能进行读写)。缓冲区的限制不能为负,并且不能大于其容量。写入模式,限制等于buffer的容量。读取模式下,limit等于写入的数据量

  • 标记 (mark)与重置 (reset) :标记是一个索引,通过 Buffer 中的 mark() 方法 指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这 个 position. *标记、位置、限制、容量遵守以下不变式:0

  • *图示:

JAVA NIO核心之缓冲区(Buffer)

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


position 到 limit 是可用区域。

假如 position=0,limit=10,capacity=10

写的时候,**0-10是可写入区域**。

如果写了5个字节,那么**5-10是可写入区域。**

调用flip,会将position赋值给limit,position变为0。即**0-5是可读区域。**

如果读完5个自己 就是 position =5 ,limit=5,capacity=10

Buffer常见方法

Buffer clear() 清空缓冲区并返回对缓冲区的引用

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


Buffer flip() 为 将缓冲区的当前位置赋值给界限,并将当前位置充值为 0

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

int capacity() 返回 Buffer 的 capacity 大小
boolean hasRemaining() 判断缓冲区中是否还有元素
int limit() 返回 Buffer的界限(limit) 的位置
Buffer limit(int n) 将设置缓冲区界限为 n, 并返回一个具有新 limit 的缓冲区对象
Buffer mark() 对缓冲区设置位置标记
public final Buffer mark()
{
       mark = position;
       return this;
  }
int position() 返回缓冲区的当前位置 position
Buffer position(int n) 将设置缓冲区的当前位置为 n , 并返回修改后的 Buffer 对象
int remaining() 返回 position 和 limit 之间的元素个数
Buffer reset() 将位置 position 转到以前设置的 mark 所在的位置。还原位置标记。
public final Buffer reset()
{
       int m = mark;
       if (m < 0)
           throw new InvalidMarkException();
       position = m;
       return this;
  }
Buffer rewind() 将位置position设为为 0, 取消设置的 mark
public final Buffer rewind()
{
       position = 0;
       mark = -1;
       return this;
  }

缓冲区的数据操作

Buffer 所有子类提供了两个用于数据操作的方法:get()put() 方法
获取Buffer中的数据
get() :读取单个字节
get(byte[] dst):批量读取多个字节到 dst 中
get(int index):读取指定索引位置的字节(不会移动 position)

将数据放到Buffer中
put(byte b):将给定单个字节写入缓冲区的当前位置
put(byte[] src):将 src 中的字节写入缓冲区的当前位置
put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动 position)

使用Buffer读写数据一般遵循以下四个步骤:

  • 1.写入数据到Buffer
  • 2.调用flip()方法,转换为读取模式
  • 3.从Buffer中读取数据
  • 4.调用buffer.clear()方法或者buffer.compact()方法清除缓冲区

案例演示

public class TestBuffer {

  public void test1(){
     String str = "itheima";

     ByteBuffer buf = ByteBuffer.allocate(1024);
     System.out.println("-----------------allocate()----------------");
     System.out.println(buf.position());
     System.out.println(buf.limit());
     System.out.println(buf.capacity());

     buf.put(str.getBytes());
     System.out.println("-----------------put()----------------");
     System.out.println(buf.position());
     System.out.println(buf.limit());
     System.out.println(buf.capacity());

     buf.flip();
     System.out.println("-----------flip(): limit = position,position=0----------");
     System.out.println(buf.position());
     System.out.println(buf.limit());
     System.out.println(buf.capacity());

     byte[] dst = new byte[buf.limit()];
     buf.get(dst);
     System.out.println(new String(dst, 0, dst.length));

     System.out.println("-----------------get()----------------");
     System.out.println(buf.position());
     System.out.println(buf.limit());
     System.out.println(buf.capacity());

     buf.rewind();
     System.out.println("-----------------rewind,只重置position归0()----------------");
     System.out.println(buf.position());
     System.out.println(buf.limit());
     System.out.println(buf.capacity());

     buf.clear();
     System.out.println("-------clear,position重置归0,limit=capacity()----------");
     System.out.println(buf.position());
     System.out.println(buf.limit());
     System.out.println(buf.capacity());
     System.out.println((char)buf.get());

      System.out.println("-------reset,position = mark----------");

  }

}

  public void test2(){
     String str = "itheima";

     ByteBuffer buf = ByteBuffer.allocate(1024);

     buf.put(str.getBytes());

     buf.flip();

     byte[] dst = new byte[buf.limit()];
     buf.get(dst, 0, 2);
     System.out.println(new String(dst, 0, 2));
     System.out.println(buf.position());

     buf.mark();

     buf.get(dst, 2, 2);
     System.out.println(new String(dst, 2, 2));
     System.out.println(buf.position());

     buf.reset();

     System.out.println(buf.position());

     if(buf.hasRemaining()){

        System.out.println(buf.remaining());
    }
  }

  public void test3(){

     ByteBuffer buf = ByteBuffer.allocateDirect(1024);
     System.out.println(buf.isDirect());
  }

直接与非直接内存

根据官方文档的描述:

byte byffer可以是两种类型,一种是基于直接内存(也就是非堆内存);另一种是非直接内存(也就是堆内存)。

对于直接内存来说,JVM将会在IO操作上具有更高的性能, 因为它直接作用于本地系统的IO操作。

非直接内存,也就是堆内存中的数据,如果要作IO操作, 会先从本进程内存复制到直接内存,再利用本地IO处理。

从数据流的角度,非直接内存是下面这样的作用链:

本地IO-->直接内存-->非直接内存-->直接内存-->本地IO

而直接内存是:

本地IO-->直接内存-->本地IO

很明显,在做IO处理时,比如网络发送大量数据时,直接内存会具有更高的效率。

直接内存使用allocateDirect创建,但是它比申请普通的堆内存需要耗费更高的性能。不过,这部分的数据是在JVM之外的,因此它不会占用应用的内存。

所以呢,当你有很大的数据要缓存,并且它的生命周期又很长,那么就比较适合使用直接内存。只是一般来说,如果不是能带来很明显的性能提升,还是推荐直接使用堆内存。字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其 isDirect() 方法来确定。

直接内存使用场景

  • 1 有很大的数据需要存储,它的生命周期又很长
  • 2 适合频繁的IO操作,比如网络并发场景

关注夏壹分享发送:资源 获取深入讲解JVM虚拟机课程JAVA NIO核心之缓冲区(Buffer)


原文始发于微信公众号(夏壹分享):JAVA NIO核心之缓冲区(Buffer)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/154863.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!