`

Java NIO工作原理(2)

阅读更多

◆  缓冲区内部细节

概  述

本节将介绍 NIO 中两个重要的缓冲区组件:状态变量和访问方法 (accessor)。

状态变量是前一节中提到的"内部统计机制"的关键。每一个读/写操作都会改变缓冲区的状态。通过记录和跟踪这些变化,缓冲区就可能够内部地管理自己的资源。

在从通道读取数据时,数据被放入到缓冲区。在有些情况下,可以将这个缓冲区直接写入另一个通道,但是在一般情况下,您还需要查看数据。这是使用 访问方法 get() 来完成的。同样,如果要将原始数据放入缓冲区中,就要使用访问方法 put()。

在本节中,您将学习关于 NIO 中的状态变量和访问方法的内容。我们将描述每一个组件,并让您有机会看到它的实际应用。虽然 NIO 的内部统计机制初看起来可能很复杂,但是您很快就会看到大部分的实际工作都已经替您完成了。您可能习惯于通过手工编码进行簿记 ― 即使用字节数组和索引变量,现在它已在 NIO 中内部地处理了。

状态变量

可以用三个值指定缓冲区在任意时刻的状态:

• position

• limit

• capacity

这三个变量一起可以跟踪缓冲区的状态和它所包含的数据。我们将在下面的小节中详细分析每一个变量,还要介绍它们如何适应典型的读/写(输入/输出)进程。在这个例子中,我们假定要将数据从一个输入通道拷贝到一个输出通道。

Position

您可以回想一下,缓冲区实际上就是美化了的数组。在从通道读取时,您将所读取的数据放到底层的数组中。 position 变量跟踪已经写了多少数据。更准确地说,它指定了下一个字节将放到数组的哪一个元素中。因此,如果您从通道中读三个字节到缓冲区中,那么缓冲区的 position 将会设置为3,指向数组中第四个元素。

同样,在写入通道时,您是从缓冲区中获取数据。 position 值跟踪从缓冲区中获取了多少数据。更准确地说,它指定下一个字节来自数组的哪一个元素。因此如果从缓冲区写了5个字节到通道中,那么缓冲区的 position 将被设置为5,指向数组的第六个元素。

Limit

limit 变量表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。

position 总是小于或者等于 limit。

Capacity

缓冲区的 capacity 表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小 ― 或者至少是指定了准许我们使用的底层数组的容量。

limit 决不能大于 capacity。

观察变量

我们首先观察一个新创建的缓冲区。出于本例子的需要,我们假设这个缓冲区的 总容量 为8个字节。 Buffer 的状态如下所示:

回想一下 ,limit 决不能大于 capacity,此例中这两个值都被设置为 8。我们通过将它们指向数组的尾部之后(如果有第8个槽,则是第8个槽所在的位置)来说明这点。

position 设置为0。如果我们读一些数据到缓冲区中,那么下一个读取的数据就进入 slot 0 。如果我们从缓冲区写一些数据,从缓冲区读取的下一个字节就来自 slot 0 。 position 设置如下所示:

由于 capacity 不会改变,所以我们在下面的讨论中可以忽略它。

第一次读取

现在我们可以开始在新创建的缓冲区上进行读/写操作。首先从输入通道中读一些数据到缓冲区中。第一次读取得到三个字节。它们被放到数组中从 position 开始的位置,这时 position 被设置为 0。读完之后,position 就增加到 3,如下所示:

limit 没有改变。

第二次读取

在第二次读取时,我们从输入通道读取另外两个字节到缓冲区中。这两个字节储存在由 position 所指定的位置上, position 因而增加 2:

limit 没有改变。

flip

现在我们要将数据写到输出通道中。在这之前,我们必须调用 flip() 方法。这个方法做两件非常重要的事:

1.它将 limit 设置为当前 position。

2.它将 position 设置为 0。

前一小节中的图显示了在 flip 之前缓冲区的情况。下面是在 flip 之后的缓冲区:

我们现在可以将数据从缓冲区写入通道了。 position 被设置为 0,这意味着我们得到的下一个字节是第一个字节。 limit 已被设置为原来的 position,这意味着它包括以前读到的所有字节,并且一个字节也不多。

第一次写入

在第一次写入时,我们从缓冲区中取四个字节并将它们写入输出通道。这使得 position 增加到 4,而 limit 不变,如下所示:

第二次写入

我们只剩下一个字节可写了。 limit在我们调用 flip() 时被设置为 5,并且 position 不能超过 limit。所以最后一次写入操作从缓冲区取出一个字节并将它写入输出通道。这使得 position 增加到 5,并保持 limit 不变,如下所示:

clear

最后一步是调用缓冲区的 clear() 方法。这个方法重设缓冲区以便接收更多的字节。 Clear 做两种非常重要的事情:

1.它将 limit 设置为与 capacity 相同。

2.它设置 position 为 0。

下图显示了在调用 clear() 后缓冲区的状态:

缓冲区现在可以接收新的数据了。

访问方法

到目前为止,我们只是使用缓冲区将数据从一个通道转移到另一个通道。然而,程序经常需要直接处理数据。例如,您可能需要将用户数据保存到磁盘。在这种情况下,您必须将这些数据直接放入缓冲区,然后用通道将缓冲区写入磁盘。

或者,您可能想要从磁盘读取用户数据。在这种情况下,您要将数据从通道读到缓冲区中,然后检查缓冲区中的数据。

在本节的最后,我们将详细分析如何使用 ByteBuffer 类的 get() 和 put() 方法直接访问缓冲区中的数据。

get() 方法

ByteBuffer 类中有四个 get() 方法:

1.byte get();

2.ByteBuffer get( byte dst[] );

3.ByteBuffer get( byte dst[], int offset, int length );

4.byte get( int index );

第一个方法获取单个字节。第二和第三个方法将一组字节读到一个数组中。第四个方法从缓冲区中的特定位置获取字节。那些返回ByteBuffer 的方法只是返回调用它们的缓冲区的 this 值。

此外,我们认为前三个 get() 方法是相对的,而最后一个方法是绝对的。 相对 意味着 get() 操作服从 limit 和 position 值 ― 更明确地说,字节是从当前 position 读取的,而 position 在 get 之后会增加。另一方面,一个 绝对 方法会忽略 limit 和 position 值,也不会影响它们。事实上,它完全绕过了缓冲区的统计方法。

上面列出的方法对应于 ByteBuffer 类。其他类有等价的 get() 方法,这些方法除了不是处理字节外,其它方面是是完全一样的,它们处理的是与该缓冲区类相适应的类型。

put()方法

ByteBuffer 类中有五个 put() 方法:

1.ByteBuffer put( byte b );

2.ByteBuffer put( byte src[] );

3.ByteBuffer put( byte src[], int offset, int length );

4.ByteBuffer put( ByteBuffer src );

5.ByteBuffer put( int index, byte b );

第一个方法 写入(put) 单个字节。第二和第三个方法写入来自一个数组的一组字节。第四个方法将数据从一个给定的源ByteBuffer 写入这个 ByteBuffer。第五个方法将字节写入缓冲区中特定的 位置 。那些返回 ByteBuffer 的方法只是返回调用它们的缓冲区的 this 值。

与 get() 方法一样,我们将把 put() 方法划分为 相对 或者 绝对 的。前四个方法是相对的,而第五个方法是绝对的。

上面显示的方法对应于 ByteBuffer 类。其他类有等价的 put() 方法,这些方法除了不是处理字节之外,其它方面是完全一样的。它们处理的是与该缓冲区类相适应的类型。

类型化的 get() 和 put() 方法

除了前些小节中描述的 get() 和 put() 方法, ByteBuffer 还有用于读写不同类型的值的其他方法,如下所示:

• getByte()

• getChar()

• getShort()

• getInt()

• getLong()

• getFloat()

• getDouble()

• putByte()

• putChar()

• putShort()

• putInt()

• putLong()

• putFloat()

• putDouble()

事实上,这其中的每个方法都有两种类型 ― 一种是相对的,另一种是绝对的。它们对于读取格式化的二进制数据(如图像文件的头部)很有用。

您可以在例子程序 TypesInByteBuffer.java 中看到这些方法的实际应用。

缓冲区的使用:一个内部循环

下面的内部循环概括了使用缓冲区将数据从输入通道拷贝到输出通道的过程。

  1. while (true) {  
  2.      buffer.clear();  
  3.      int r = fcin.read( buffer );  
  4.       if (r==-1) {  
  5.        break;  
  6.      }  
  7.       buffer.flip();  
  8.      fcout.write( buffer );} 

read() 和 write() 调用得到了极大的简化,因为许多工作细节都由缓冲区完成了。 clear() 和 flip() 方法用于让缓冲区在读和写之间切换。

分享到:
评论

相关推荐

    Java NIO原理 图文分析及代码实现

    ### Java NIO原理 图文分析及代码实现 #### 前言 在深入探讨Java NIO之前,我们先简要回顾一下NIO的概念及其引入的原因。随着互联网的发展,越来越多的应用程序需要处理高并发的网络连接请求。传统的阻塞I/O模型在...

    java NIO技巧及原理

    **Java IO原理:** Java IO基于流模型,分为输入流和输出流。流是一维数据序列,可以是从源到目标的单向流动。IO操作是阻塞的,即当一个线程执行读或写操作时,如果数据未准备好,线程会被阻塞,直到数据准备就绪。...

    Java NIO的介绍及工作原理

    ### Java NIO的介绍及工作原理 #### 一、引言 Java NIO(New I/O)是Java 1.4版本引入的一个重要的I/O处理框架,它为Java应用程序提供了处理I/O操作的新方法。与传统的Java IO模型相比,NIO提供了一种更加高效的...

    java NIO原理和使用

    在深入了解 Java NIO 的工作原理及其使用之前,我们首先来了解一下什么是 Java NIO(New I/O)。Java NIO 是 Java SE 1.4 版本引入的一个全新的 I/O API,用来替代传统的 I/O 类型,即 java.io 包下的类。 Java NIO...

    Java NIO原理解析

    Java NIO,即Non-Blocking I/O,是Java在JDK 1.4引入的一套新的I/O API,旨在提供一种...随着Java版本的更新,NIO的功能也在不断完善,如NIO 2引入了异步文件I/O和Path API,进一步增强了Java在低级I/O操作上的表现。

    Java NIO原理分析及代码实例

    理解并掌握NIO的原理和使用,对于提升Java应用的性能和可扩展性至关重要。通过以上介绍的知识点,你可以开始编写基于NIO的应用,例如使用SocketChannel实现一个简单的非阻塞服务器。在实际编码时,参考博文链接中的...

    java NIO实例

    下面将详细介绍Java NIO的主要组件和工作原理,并结合这两个文件名推测它们可能包含的内容。 1. **Selector(选择器)**:选择器是NIO的核心组件,它能够监控多个通道(Channel)的状态变化,当某个通道准备进行...

    一个java NIO的例子

    下面我们将深入探讨Java NIO的关键组件和工作原理。 1. **通道(Channel)**:通道是数据传输的途径,类似于传统的流。Java NIO提供了多种类型的通道,如FileChannel用于文件操作,SocketChannel和...

    Java NIO工作原理的全面分析

    Java NIO(New Input/Output)是自JDK 1.4版本开始引入的一种I/O模型,相较于传统的基于流的I/O(IO),NIO提供了一种面向块的、高效的数据...理解和掌握NIO的工作原理,能够帮助开发者编写出性能更好的Java应用程序。

    Java.NIO资源下载资源下载

    - **Selector 基础**:介绍了 Selector 的基本概念及工作原理。 - **使用 SelectionKey**:解释了如何使用 SelectionKey 来管理 Channel 的选择状态。 - **使用 Selectors**:详细讨论了如何使用 Selector 来监听多...

    java nio示例代码

    这些示例通常会包含简单的读写文件、服务器端与客户端的通信以及多路复用的使用,帮助初学者快速理解NIO的工作原理和实际应用。在学习过程中,你可以逐步深入,从基础的Buffer操作到复杂的Selector机制,掌握Java ...

    java NIO详细教程

    ### Java NIO 详细教程知识点解析 #### 一、Java NIO 概述 Java NIO(New IO)是Java平台提供的一种新的IO操作模式,它首次出现在Java 1.4版本中,并在后续版本中不断完善。Java NIO 的设计目的是为了克服传统Java ...

    Java NIO原理和使用

    Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,...

    JavaNIO的原理.pdf

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java从JDK 1.4版本开始引入的一种I/O模型,它为Java提供了更高效的数据传输能力,尤其适用于高并发的I/O操作场景。与传统的 Blocking I/O 相比,...

    Java NIO学习资料+代码.zip

    NIO的工作原理** 在NIO中,首先创建一个或多个通道,并将这些通道注册到选择器上。然后,主线程通过选择器监听所有通道的事件,如连接就绪、数据可读、写操作就绪等。当某个通道准备就绪时,选择器会返回这个通道...

    java nio聊天室源码

    Java NIO(New IO)是Java 1.4版本引入的一种新的I/O API,它提供了非阻塞I/O操作的能力,极大地提升了Java在...通过分析和学习这个源码,开发者可以深入理解Java NIO的工作原理,并将其应用于实际的网络编程项目中。

    Java NIO——Selector机制解析三(源码分析)

    本文将深入探讨Java NIO中的Selector机制,并通过源码分析来理解其实现原理。 Selector机制是Java NIO中的核心组件,它允许单线程同时监控多个通道(Channels)的状态变化,例如连接就绪、数据可读或可写等。这种...

Global site tag (gtag.js) - Google Analytics