zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Java NIO源码分析之Buffer

JAVAnio源码 分析 Buffer
2023-09-27 14:29:33 时间

        Java NIO的主要读写处理逻辑就是将数据从通道读入缓冲区,从缓冲区写入到通道中。而这个数据缓冲区的基类就是Buffer。而Buffer本质上就是一块可读写数据的内存,其提供了一些方法,方便外部调用者访问这块内存进行数据读写操作。

        使用Buffer读写数据的主要步骤,大体如下:

        1、将数据写入Buffer;

        2、调用flip()方法,将读写模式由写模式切换成读模式;

        3、从Buffer中读取数据;

        4、调用clear()方法或者compact()方法清空缓冲区,完成数据读操作。


        Buffer中三个十分重要的属性

        1、capacity

        代表了Buffer的最大容量,标识出Buffer中最多可以存储的capacity个byte、long、char等类型的数据;

        2、position

        代表了缓冲区Buffer当前待写入或待读取位置,初始值为0,当一个byte、long、char等数据被写入或者被读取后,position自动移动到下一个可写入位置,其最大值为capacity – 1,实际上它受limit限制。一般情况下,Buffer从写模式切换到读模式或者从读模式切换到写模式,position均会被重置为0;

        3、limit

        表示可写入或可读取数据的限制。在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据,limit等于Buffer的capacity。在读模式下,limit表示你最多能读到多少数据,当切换Buffer到读模式时,limit会被设置成写模式下的position值,也就是说你能读到之前写入的所有数据。


        Buffer中,获取这些属性的方法分别是:capacity()、position()、limit()方法,而position、limit也有对应方法进行设置,下面的源码分析我们会分别介绍。

        Byffer中,一个不可变的定律是:mark = position = limit = capacity。

        

        初始情况下:

        mark = -1

        position = 0

        limit = capacity - 1

        capacity = m 


        写入n个数据时:

        mark = -1

        position = n

        limit = capacity - 1

        capacity = m(m = n)


        flip()切换写模式至读模式时:

        mark = -1

        position = 0

        limit = position(也就是n)

        capacity = m(m = n)


        读取k个数据时

        mark = -1

        position = k

        limit = position(也就是n)

        capacity = m(m = n)


        Buffer有两种模式:读模式和写模式。这两种模式的切换是是通过以下方法完成的:

        1、写模式到读模式:flip()

        2、读模式到写模式:clear()或compact()(compact()方法为ByteBuffer中的抽象方法)


        Buffer源码分析:

        flip()方法

 /**

 * Flips this buffer. The limit is set to the current position and then

 * the position is set to zero. If the mark is defined then it is

 * discarded.

 * 翻转这个缓冲区。limit被设置为当前位置position,而当前位置position被设置为0。如果标记mark被定义,此时它将被丢弃。

 * p After a sequence of channel-read or i put /i operations, invoke

 * this method to prepare for a sequence of channel-write or relative

 * i get /i operations. For example:

 * 在一系列管道读channel-read或写入put操作之后,调用该方法为一系列管道写channel-write或相关读取get操作做准备。比如:

 * blockquote pre 

 * buf.put(magic); // Prepend header

 * in.read(buf); // Read data into rest of buffer

 * buf.flip(); // Flip buffer

 * out.write(buf); // Write header + data to channel /pre /blockquote 

 * 调用buf的put()方法预先放入header数据;

 * 调用输入流的read()方法读取数据并写入到缓冲区;

 * 翻转buffer:调用buf的flip()将buf由写模式切换到读模式;

 * 调用输出流的write()方法将数据从缓冲区中读取到输出流;

 * p This method is often used in conjunction with the {@link

 * java.nio.ByteBuffer#compact compact} method when transferring data from

 * one place to another. /p 

 * 当将数据从一个地方转移到另一个地方时,这个方法往往是结合ByteBuffer的compact()方法使用。

 * @return This buffer

 public final Buffer flip() {

 // 将limit设置为当前位置position

 limit = position;

 // 当前位置position设置为0

 position = 0;

 // 标记mark设置为-1,即无效

 mark = -1;

 // 返回当前Buffer实例

 return this;

 }
        flip()实际上是翻转数据缓冲区Buffer,将其由写模式转换成读模式。大体逻辑如下:

        1、将limit设置为当前位置position,标识出读模式下最大可读取数据限制为之前已写入数据;

        2、当前位置position设置为0,标识可读取数据的起始位置;

        3、标记mark设置为-1,即无效;

        4、返回当前Buffer实例。


        clear()方法

 /**

 * Clears this buffer. The position is set to zero, the limit is set to

 * the capacity, and the mark is discarded.

 * 清空这个缓冲区buffer。当前位置position被设置为0,读写限制被设置成缓冲区buffer的最大容量capacity,标记mark被设置为无效。

 * p Invoke this method before using a sequence of channel-read or

 * i put /i operations to fill this buffer. For example:

 * 在使用一系列管道读或者写数据put操作填充该缓冲区buffer前调用该方法。比如:

 * blockquote pre 

 * buf.clear(); // Prepare buffer for reading

 * in.read(buf); // Read data /pre /blockquote 

 * 调用缓冲区buf的clear()方法为管道读并写入数据做准备;

 * 调用输入流in的read()方法将数据从管道读出并写入到缓冲区buf;

 * p This method does not actually erase the data in the buffer, but it

 * is named as if it did because it will most often be used in situations

 * in which that might as well be the case. /p 

 * 这个方法并不实际删除缓冲区中的数据

 * @return This buffer

 public final Buffer clear() {

 // 当前位置position设置为0

 position = 0;

 // 读写限制limit设置为buffer的最大容量capacity

 limit = capacity;

 // 标记mark设置为-1,即无效

 mark = -1;

 // 返回当前Buffer实例

 return this;

 }
        clear()方法看上去像是清空缓冲区buffer,但是这个方法并不实际删除缓冲区中的数据,而是将其由读模式转换成写模式。它的主要逻辑是:

        1、当前位置position设置为0,又可以从0开始写入数据;

        2、读写限制limit设置为buffer的最大容量capacity,即我们可以写入数据至完全填充整个缓冲区buffer;

        3、标记mark设置为-1,即无效;

        4、返回当前Buffer实例。

        

        position()方法

 /**

 * Returns this buffers position. /p 

 * 返回当前缓冲区buffer的当前可写或可读位置position

 * @return The position of this buffer

 public final int position() {

 return position;

 }

        position()方法用于获取当前缓冲区buffer的当前可写或可读位置position。

        

        position(int newPosition)方法
 /**

 * Sets this buffers position. If the mark is defined and larger than the

 * new position then it is discarded. /p 

 * 设置当前缓冲区buffer的当前可写或可读位置position。如果标记mark被定义,而且大于需要被设置的新位置newPosition,那么它会被标记为无效。

 * @param newPosition

 * The new position value; must be non-negative

 * and no larger than the current limit

 * @return This buffer

 * @throws IllegalArgumentException

 * If the preconditions on tt newPosition /tt do not hold

 public final Buffer position(int newPosition) {

 // 如果需要被设置的新位置newPosition大于limit或者小于0,直接抛出参数不合法IllegalArgumentException异常

 if ((newPosition limit) || (newPosition 0))

 throw new IllegalArgumentException();

 // 当前位置position被设置为newPosition

 position = newPosition;

 // 如果标记mark大于newPosition,标记mark被设置为-1,即无效

 if (mark position) mark = -1;

 // 返回当前缓冲区buffer实例this

 return this;

 }

         

        rewind()方法

        一般情况下,flip只能被调用一次,如果是数据需要被重新读入,怎么办?这时rewind()方法就被派上用场了,其代码如下:

 /**

 * Rewinds this buffer. The position is set to zero and the mark is

 * discarded.

 * 重绕(倒回)这个缓冲区buffer。当前位置position被设置为0,标记mark被设置为无效。

 * p Invoke this method before a sequence of channel-write or i get /i 

 * operations, assuming that the limit has already been set

 * appropriately. For example:

 * 假设限制limit已经被正确设置,在一系列管道写channel-write或读取get操作之前调用该方法。比如:

 * blockquote pre 

 * out.write(buf); // Write remaining data

 * buf.rewind(); // Rewind buffer

 * buf.get(array); // Copy data into array /pre /blockquote 

 * 从缓冲区buf中读取数据并写入输出流out;

 * 调用缓冲区buf的rewind()方法,重绕这个缓冲区

 * 将缓冲区buf中数据获取,复制到arrary

 * @return This buffer

 public final Buffer rewind() {

 // 当前位置position被设置为0

 position = 0;

 // 标记mark被设置为无效

 mark = -1;

 // 返回当前缓冲区buffer实例this

 return this;

 }



        




如何使用 Java 中 缓冲区类 Buffer # 如何使用 Java 中 缓冲区类 Buffer ## 1. 什么是Buffer 缓冲区 缓冲区(Buffer):就是在内存中预留指定大小的存储空间用来对输入/输出(I/O)的数据作临时存储,这部分预留的内存空间就叫做缓冲区 缓冲区本质上是一个可以读写数据的内存块,可以理解成是一个数组,该对象提供了一组方法,可以更轻松地使用内存块 ## 2.Buffer及其常用子类 从 JDK1.4开始,提供使用Buffer类
Java NIO 概述(Channel、Buffer、Selector) Java NIO (New IO 或 Non Bloking IO) 是从 Java 1.4 版本开始引入一个新的 IO API, 可以替代标准的 Java IO API。NIO 全面支持面向缓冲区的、基于通道的 IO 操作。NIO 将以高效的方式进行文件的读写操作。
Java NIO三件套之Buffer实现原理解析 目前很多高性能的Java RPC框架都是基于Netty实现的,而Netty的设计原理又离不开Java NIO。本篇笔记是对NIO核心三件套:缓冲区(Buffer)、选择器 (Selector)和通道(Channel),其中之一的缓冲区Buffer实现原理的学习总结