NIO的核心对象之Buffer(缓冲)

面向流的I/O通常是非常慢的,而且InputStream使用的read方法从流中读取数据时,如果数据源中没有数据,它将会一直等待,处于阻塞状态,那么程序中其他处理将会被影响。为了解决I/O流的一些问题,就有了NIO,它新增了很多用于处理I/O的类,并且java.io包中的很多类以NIO为基础进行了改写。NIO与原来的I/O有同样的功能和目的,但是处理I/O的方式不同,块I/O在效率上比流I/O高许多。

Channel(通道)和Buffer(缓冲)是NIO中的核心对象。其中,Buffer可以看作是一个容器对象,它的本质是一个数组,发送给一个Chnnel的所有对象都必须先放到Buffer中,而从通道中读取的数据,也要先读到Buffer中,这就是NIO与之前的I/O不同的一个地方。在原来的java.io包中,对数据的操作全都是直接写入Stream中,或者从中取出。需要注意的是,Buffer是非线程安全类,非线程安全类效率较高。

最常用的Buffer类是ByteBuffer,他可以在底层字节数组上进行get/set操作。除了ByteBuffer外,对应其他简单的数据类型都有相应的缓冲类:

  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

Boolean类型除外。ByteBuffer是唯一支持对其他类型进行读写的缓冲类,因为其他类都是特定于类型的。一旦连接上,就可以使用ByteBuffer对象从通道中读取数据或者从中取出。

/**
 * <code>UNSET_MARK</code> means the mark has not been set.
 */
static final int UNSET_MARK = -1;

/**
 * The capacity of this buffer, which never changes.
 */
final int capacity;

/**
 * <code>limit - 1</code> is the last element that can be read or written.
 * Limit must be no less than zero and no greater than <code>capacity</code>.
 */
int limit;

/**
 * Mark is where position will be set when <code>reset()</code> is called.
 * Mark is not set by default. Mark is always no less than zero and no
 * greater than <code>position</code>.
 */
int mark = UNSET_MARK;

/**
 * The current position of this buffer. Position is always no less than zero
 * and no greater than <code>limit</code>.
 */
int position = 0;

/**
 * The log base 2 of the element size of this buffer.  Each typed subclass
 * (ByteBuffer, CharBuffer, etc.) is responsible for initializing this
 * value.  The value is used by JNI code in frameworks/base/ to avoid the
 * need for costly 'instanceof' tests.
 */
final int _elementSizeShift;

/**
 * For direct buffers, the effective address of the data; zero otherwise.
 * This is set in the constructor.
 */
final long effectiveDirectAddress;

ByteBuffer中,容量(capacity)、界限(limit)、位置(position)、标记(mark)是三个重要概念:

capacity

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

capacity一旦初始化后就不会改变,也不可能为负值,其值一直为常量。在使用中我们一般使用Buffer的抽象子类ByteBuffer.allocate()方法,实际上是生成ByteArrayBuffer类。

limit

第一个不应该被读出或者写入的缓冲区位置索引。

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

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

position

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

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

Mark

一个备忘位置。标记在设定前是未定义的(undefined)。使用场景是,假设缓冲区中有 10 个元素,position 目前的位置为 2(也就是如果get的话是第三个元素),现在只想发送 6 - 10 之间的缓冲数据,此时我们可以 buffer.mark(buffer.position()),即把当前的 position 记入 mark 中,然后 buffer.postion(6),此时发送给 channel 的数据就是 6 - 10 的数据。发送完后,我们可以调用 buffer.reset() 使得 position = mark,因此这里的 mark 只是用于临时记录一下位置用的。

在使用 Buffer 时,我们实际操作的就是这四个属性的值。我们发现,Buffer 类并没有包括 get() 或 put() 函数。但是,每一个Buffer 的子类都有这两个函数,但它们所采用的参数类型,以及它们返回的数据类型,对每个子类来说都是唯一的,所以它们不能在顶层 Buffer 类中被抽象地声明。它们的定义必须被特定类型的子类所遵从。若不加特殊说明,我们在下面讨论的一些内容,都是以 ByteBuffer 为例,当然,它当然有 get() 和 put() 方法了。 

public abstract class ByteBuffer extends Buffer implements Comparable {   
// This is a partial API listing  
public abstract byte get( );    
public abstract byte get (int index);    
public abstract ByteBuffer put (byte b);    
public abstract ByteBuffer put (int index, byte b);   
}   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值