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

本文讲解了Java NIO中Buffer的内部工作原理,包括状态变量(position、limit、capacity)的作用及如何通过flip()和clear()方法切换读写模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java NIO 入门(四)Buffer内部原理
guibin.beijing@gmail.com

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

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

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

[size=medium]状态变量[/size]
总共有三个值可以被用来表示在给定的任何时刻Buffer的状态,他们分别是:
[list]
[*]position
[*]limit
[*]capacity
[/list]
这三个变量跟踪了Buffer的状态和Buffer所包含的数据。
接下来我们将逐一检查每个细节,并且也看看为什么这样的设计适合典型的读/写(输入/输出)处理。比如仅仅这个例子,我们假设从一个Channel拷贝数据到另一个Channel。

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

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

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

[size=medium]以实例来观察这三个变量[/size]
我们从一个新建的Buffer开始。由于是例子的缘故,我们假设Buffer有一个8字节大小的Capacity。此时Buffer的状态如下所示:
[img]http://dl.iteye.com/upload/attachment/553345/ad8f6af7-3b54-321c-a24f-48acd98f4ecf.png[/img]

回忆之前所讲的,limit不会大于capacity,在这个例子中,limit和capacity都会被设为8。我们通过在array尾部用箭头指示的方式表示。
[img]http://dl.iteye.com/upload/attachment/553351/b7af0315-5b3a-3a43-8008-a52c60258d1e.png[/img]

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

[img]http://dl.iteye.com/upload/attachment/553353/ca9d0263-c6e1-358b-a63b-e6df55834420.png[/img]

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

[size=medium]第一次读操作[/size]
现在我们准备好了在我们新建的Buffer上开始读/写操作了。我们开始从Channel中读一些数据进入Buffer,第一次读3个字节。读之前position为0,读完后position从0增长到3,如下所示:
[img]http://dl.iteye.com/upload/attachment/553356/46108444-93d9-3696-af39-e5b9f1f993e9.png[/img]

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

[img]http://dl.iteye.com/upload/attachment/553359/f488db0c-fae5-38c4-bf56-fc298138b017.png[/img]

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

[img]http://dl.iteye.com/upload/attachment/553371/df701f24-5f5d-3400-8c0d-87ca2d716dcf.png[/img]

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

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

[img]http://dl.iteye.com/upload/attachment/553374/90c263fd-8386-3e65-8da6-fafe91441b41.png[/img]

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

[img]http://dl.iteye.com/upload/attachment/553425/040df516-bc16-3655-b0e4-83d74327f2a2.png[/img]

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

[img]http://dl.iteye.com/upload/attachment/553447/9c7f18ba-afb2-3cb4-b7d7-69d190aaf5e6.png[/img]

[size=medium]一个能工作的Buffer[/size]
下面的代码总结了使用Buffer从一个Channel拷贝数据到另一个Channel

while(trie) {
buffer.clear();
int r = fcin.read( buffer );

if (r==-1) {
break;
}

buffer.flip();
fcout.write( buffer );
}

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

本文参考自[url]http://www.cs.brown.edu/courses/cs161/papers/j-nio-ltr.pdf[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值