当前位置:编程学习 > JAVA >>

Java NIO入门(四)Buffer内部原理

概述
在这节中,我们将关注NIO的Buffer中两个重要的组件:状态变量和访问方法。
状态变量对于前面提到的“内部计数系统”而言相当重要,每次进行完读写之后,Buffer的状态都随之改变。通过记录和跟踪这些改变,Buffer才可以把Buffer内部的资源管理好。

当你从Channel中读数据时,数据首先放到了Buffer中。在某些情况下,你可以直接把这个Buffer写入另一个Channel中,但是通常情况下,你可能想看看数据内容,这个想法可以通过方法get()实现。相似的,当你想要把原始数据放进Buffer中,你可以使用put()方法。

在这节中,我们将学习NIO中的状态变量和访问方法。每个组件都会涉及到,并且有机会查看具体的使用。然而NIO的内部计数系统起初看起来可能有些复杂,你很快会看到内部计数系统实际上为你做了哪些事情。

状态变量
总共有三个值可以被用来表示在给定的任何时刻Buffer的状态,他们分别是:
position
limit
capacity

这三个变量跟踪了Buffer的状态和Buffer所包含的数据。
接下来我们将逐一检查每个细节,并且也看看为什么这样的设计适合典型的读/写(输入/输出)处理。比如仅仅这个例子,我们假设从一个Channel拷贝数据到另一个Channel。

Position
回忆一下,Buffer实际上也就是个array。当你从Channel中读数据时,你把从Channel中读出来的数据放进底层array,position变量用来跟踪截止目前为止已经写了多少数据。更精确的讲,它指示如果下次写Buffer时数据应该进入array的哪个位置。因此如果已经从Channel中读出了3个字节,Buffer的position会被置为3,指向array中第四个位置。
相似的,如果正在向Channel中写入数据,你需要从Buffer中获取要写的数据,此时position持续的跟踪你已经从Buffer中读取了多少数据。更精确的说,position指示了下一次从Buffer中读取数据时将读入array的哪个元素。因此如果你已经向Channel中写了5个byte,Buffer的position被置为5,指向array中第六个元素。

Limit
在从Buffer中向Channel中写数据时,limit变量指示了还剩多少数据可以读取,在从Channel中读取数据到Buffer中时,limit变量指示了还剩多少空间可供存放数据。
position正常情况下小于或者等于limit。

Capacity
Buffer的Capacity指示Buffer最多能够存储的数据。实际上,它指示了底层array的容量,或者至少是底层array允许使用的空间数量。
Limit永远不会大于capacity。

以实例来观察这三个变量
我们从一个新建的Buffer开始。由于是例子的缘故,我们假设Buffer有一个8字节大小的Capacity。此时Buffer的状态如下所示:
 

回忆之前所讲的,limit不会大于capacity,在这个例子中,limit和capacity都会被设为8。我们通过在array尾部用箭头指示的方式表示。
 

此时position设置为0。如果我们从Channel读了一些数据进入Buffer,下一个字节将会被存入位置为0的地方。如果我们从Buffer中写数据进入Channel,Buffer中下一个被读的字节将从位置0取得。position的设置如下图所示:

 

因为capacity一旦设置好就不会改变了,之后的讨论我们将暂时忽略capacity。

第一次读操作
现在我们准备好了在我们新建的Buffer上开始读/写操作了。我们开始从Channel中读一些数据进入Buffer,第一次读3个字节。读之前position为0,读完后position从0增长到3,如下所示:
 

第二次读操作
第二次读操作,我们将再读2个字节从Channel到Buffer中,这两个字节存储的位置从之前的position即3开始,读完后position增加了2变为5。如下图:

 

Flip操作
到目前位置,我们结束了从Channel中读的操作,现在开始将数据写入输出Channel中。在做写操作之前,我们必须调用一次flip()方法,这个方法做了两件重要的事情:
1. 将limit设置到当前的position处。
2. 设置position为0。
上幅图展示了在执行flip之前的Buffer,下面这幅图展示了执行了flip() 之后的Buffer:

 

现在我们已经准备好了把数据从Buffer中写到Channel中。Position已经设置为0,意思是说我们将获得的下一个字节为位置为0的字节,limit已经设置到了之前的position处,意思是此时Buffer中包含有之前读入Buffer中的所有字节。

第一次写操作
在我们第一次的写操作中,我们从Buffer中取出4个字节,然后写入output channel中。这个操作使得position从0增加到4,而limit没有变化,如下所示:

 

第二次写操作
目前为止,只剩一个字节可写了。当我们调用flip()时,limit被设置成5,并且position不能超越limit。因此最后的写操作从Buffer取出一个字节并写入output Channel中。这次写操作会将position增加到5,而limit不变。如下所示:

 

Clear操作
在我们的最后一步是调用Clear方法。这个方法会重置Buffer以准备接收新数据。Clear做了2件重要的事情:
1. 设置limit为0以匹配capacity。
2. 设置position为0。
下面的图展示了当调用完flip()之后的Buffer的状态:

 

一个能工作的Buffer
下面的代码总结了使用Buffer从一个Channel拷贝数据到另一个Channel
Java代码 
while(trie) { 
  buffer.clear(); 
  int r = fcin.read( buffer ); 
 
  if (r==-1) { 
    break; 
  } 
 
  buffer.flip(); 
  fcout.write( buffer ); 

read()和write()方法极大的简化了程序,由于Buffer处理了所有的细节。clear()和flip()方法用来切换Buffer的读和写。

作者“guibin”
 

补充:软件开发 , Java ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,