关于NIO笔记(二):缓冲区Buffer

来源:互联网 发布:淘宝卖家如何借贷 编辑:程序博客网 时间:2024/05/23 01:11
 Java NIO系统的和兴在于:通道(Channel)和缓冲区(Buffer)。通道表示打开到IO设备(例如:文件、套接字)的连接。若需要使用NIO系统,需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。
简单的说就是,Channel负责传输,Buffer负责存储。
缓冲器(Buffer)

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

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

  1. 写入数据到Buffer
  2. 调用flip()方法
  3. 从Buffer中读取数据
  4. 调用clear()方法或者compact()方法

当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。

一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。

为了理解Buffer的工作原理,需要熟悉它的五个属性:

  • capacity
  • position
  • limit
  • mark
  • reset

position和limit的含义取决于Buffer处在读模式还是写模式。不管Buffer处在什么模式,capacity的含义总是一样的。

 

capacity

作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。

position

当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.

当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。

limit

在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。

当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)

mark与reset

标记是一个索引,通过Buffer中的mark()方法指定Buffer中一个特定的position,之后可以调用reset()方法恢复到这个position中。
之间的关系:0 <= mark <= position <= limit <= capacity

  1. @Test
  2. public void test1(){
  3. String str = "abcde";
  4. //1. 分配一个指定大小的缓冲区
  5. ByteBuffer buf = ByteBuffer.allocate(1024);
  6. System.out.println("-----------------allocate()----------------");
  7. System.out.println(buf.position());
  8. System.out.println(buf.limit());
  9. System.out.println(buf.capacity());
  10. //2. 利用 put() 存入数据到缓冲区中
  11. buf.put(str.getBytes());
  12. System.out.println("-----------------put()----------------");
  13. System.out.println(buf.position());
  14. System.out.println(buf.limit());
  15. System.out.println(buf.capacity());
  16. //3. 切换读取数据模式
  17. buf.flip();
  18. System.out.println("-----------------flip()----------------");
  19. System.out.println(buf.position());
  20. System.out.println(buf.limit());
  21. System.out.println(buf.capacity());
  22. //4. 利用 get() 读取缓冲区中的数据
  23. byte[] dst = new byte[buf.limit()];
  24. buf.get(dst);
  25. System.out.println(new String(dst, 0, dst.length));
  26. System.out.println("-----------------get()----------------");
  27. System.out.println(buf.position());
  28. System.out.println(buf.limit());
  29. System.out.println(buf.capacity());
  30. //5. rewind() : 可重复读
  31. buf.rewind();
  32. System.out.println("-----------------rewind()----------------");
  33. System.out.println(buf.position());
  34. System.out.println(buf.limit());
  35. System.out.println(buf.capacity());
  36. //6. clear() : 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态
  37. buf.clear();
  38. System.out.println("-----------------clear()----------------");
  39. System.out.println(buf.position());
  40. System.out.println(buf.limit());
  41. System.out.println(buf.capacity());
  42. System.out.println((char)buf.get());
  43. }

  1. @Test
  2. public void test2(){
  3. String str = "abcde";
  4. //获取1024字节的存储空间
  5. ByteBuffer buf = ByteBuffer.allocate(1024);
  6. //存放
  7. /**
  8. * 二、缓冲区存取数据的两个核心方法:
  9. * put() : 存入数据到缓冲区中
  10. * get() : 获取缓冲区中的数据
  11. *
  12. */
  13. buf.put(str.getBytes());
  14. //切换读写模式
  15. buf.flip();
  16. byte[] dst = new byte[buf.limit()];
  17. /**get()源码
  18. * public ByteBuffer get(byte[] dst, int offset, int length) {
  19. checkBounds(offset, length, dst.length);
  20. if (length > remaining())
  21. throw new BufferUnderflowException();
  22. int end = offset + length;
  23. for (int i = offset; i < end; i++)
  24. dst[i] = get();
  25. return this;
  26. }
  27. */
  28. buf.get(dst, 0, 2);
  29. System.out.println(new String(dst, 0, 2));//ab
  30. System.out.println(buf.position());//2
  31. //mark() : 标记
  32. buf.mark();
  33. buf.get(dst, 2, 2);
  34. System.out.println(new String(dst, 2, 2));//cd
  35. System.out.println(buf.position());//4
  36. //reset() : 恢复到 mark 的位置
  37. buf.reset();
  38. System.out.println(buf.position());//2
  39. //判断缓冲区中是否还有剩余数据
  40. if(buf.hasRemaining()){
  41. //获取缓冲区中可以操作的数量
  42. System.out.println(buf.remaining());//3
  43. }
  44. }

二:直接缓冲区和非直接缓冲区
  • 字节缓冲要么是直接的,要么是非直接的。如果为直接字节缓冲区,则Java虚拟机会尽最大努力直接在缓冲区上执行本机I/O操作,也就是说,在每次调用基础操作系统的一个本机I/O操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中。
  • 直接字节可以调用allocateDirect()工厂方法来创建,此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓存去。直接缓冲区的内容可以在驻留的垃圾回收堆之外,因此,他们对应用程序的内存需求量造成的影响可能并不明显,所以最好在直接缓冲区能在程序性能方面带来明显好处时分配他们。
  • 直接字节缓冲去还可以通过fileChannel的map()方法将文件区域直接映射到内存中创建。该方法返回的是MappedByteBuffer。java平台的事项有助于通过JNI从本机代码创建直接字节缓冲区。如果以上这些缓冲区中的某一缓冲区实例指的是不可访问的内存区域,则试图访问该区域不会更改缓冲区的内容,并且将会在访问期间或稍后的某一个时间导致抛出不确定的异常。
  • 字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用isDirect()方法来确定。提供此方法是为了能够在性能关键型代码中执行显示缓冲区管理。

 

 


  1. @Test
  2. public void test3(){
  3. /**
  4. * * 非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中
  5. * 直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率
  6. */
  7. //分配直接缓冲区
  8. ByteBuffer buf = ByteBuffer.allocateDirect(1024);
  9. System.out.println(buf.isDirect());//true
  10. }

 来源:网上资料,视频资料,学习总结所用。
 

0 0
原创粉丝点击