Java NIO源码分析之Buffer

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

        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 buffer's position. </p>
     * 返回当前缓冲区buffer的当前可写或可读位置position
     *
     * @return  The position of this buffer
     */
    public final int position() {
        return position;
    }

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

        

        position(int newPosition)方法
    /**
     * Sets this buffer's 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;
    }



        



相关文章
|
4月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
19天前
|
消息中间件 缓存 Java
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。
java nio,netty,kafka 中经常提到“零拷贝”到底是什么?
|
1月前
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
31 2
|
2月前
|
存储 网络协议 Java
Java NIO 开发
本文介绍了Java NIO(New IO)及其主要组件,包括Channel、Buffer和Selector,并对比了NIO与传统IO的优势。文章详细讲解了FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel及Pipe.SinkChannel和Pipe.SourceChannel等Channel实现类,并提供了示例代码。通过这些示例,读者可以了解如何使用不同类型的通道进行数据读写操作。
Java NIO 开发
|
1月前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
27 1
|
1月前
|
缓存 Java
java文件读取 while ((len = reader.read(buffer)) != -1){}的理解
本文解释了Java中使用`InputStreamReader`和`read(buffer)`方法循环读取文件内容的机制,强调了如何正确理解读取循环和处理读取到的数据,以及如何处理字符编码和换行符。
45 0
|
3月前
|
Java
"揭秘Java IO三大模式:BIO、NIO、AIO背后的秘密!为何AIO成为高并发时代的宠儿,你的选择对了吗?"
【8月更文挑战第19天】在Java的IO编程中,BIO、NIO与AIO代表了三种不同的IO处理机制。BIO采用同步阻塞模型,每个连接需单独线程处理,适用于连接少且稳定的场景。NIO引入了非阻塞性质,利用Channel、Buffer与Selector实现多路复用,提升了效率与吞吐量。AIO则是真正的异步IO,在JDK 7中引入,通过回调或Future机制在IO操作完成后通知应用,适合高并发场景。选择合适的模型对构建高效网络应用至关重要。
89 2
|
3月前
|
网络协议 Java 应用服务中间件
Tomcat源码分析 (一)----- 手撕Java Web服务器需要准备哪些工作
本文探讨了后端开发中Web服务器的重要性,特别是Tomcat框架的地位与作用。通过解析Tomcat的内部机制,文章引导读者理解其复杂性,并提出了一种实践方式——手工构建简易Web服务器,以此加深对Web服务器运作原理的认识。文章还详细介绍了HTTP协议的工作流程,包括请求与响应的具体格式,并通过Socket编程在Java中的应用实例,展示了客户端与服务器间的数据交换过程。最后,通过一个简单的Java Web服务器实现案例,说明了如何处理HTTP请求及响应,强调虽然构建基本的Web服务器相对直接,但诸如Tomcat这样的成熟框架提供了更为丰富和必要的功能。
|
3月前
|
存储 缓存 安全
深度剖析Java HashMap:源码分析、线程安全与最佳实践
深度剖析Java HashMap:源码分析、线程安全与最佳实践
|
3月前
|
网络协议 C# 开发者
WPF与Socket编程的完美邂逅:打造流畅网络通信体验——从客户端到服务器端,手把手教你实现基于Socket的实时数据交换
【8月更文挑战第31天】网络通信在现代应用中至关重要,Socket编程作为其实现基础,即便在主要用于桌面应用的Windows Presentation Foundation(WPF)中也发挥着重要作用。本文通过最佳实践,详细介绍如何在WPF应用中利用Socket实现网络通信,包括创建WPF项目、设计用户界面、实现Socket通信逻辑及搭建简单服务器端的全过程。具体步骤涵盖从UI设计到前后端交互的各个环节,并附有详尽示例代码,助力WPF开发者掌握这一关键技术,拓展应用程序的功能与实用性。
121 0
下一篇
无影云桌面