`

全面解读Java NIO工作原理(2)

    博客分类:
  • Java
阅读更多

 

JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的、面向块的 I/O。本实用教程从高级概念到底层的编程细节,非常详细地介绍了 NIO 库。您将学到诸如缓冲区和通道这样的关键 I/O 元素的知识,并考察更新后的库中的标准 I/O 是如何工作的。您还将了解只能通过 NIO 来完成的工作,如异步 I/O 和直接缓冲区。

Sky:

 

◆  缓冲区内部细节

概  述

本节将介绍 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() 方法用于让缓冲区在读和写之间切换。

分享到:
评论

相关推荐

    dubbo最新全面深度解读

    本篇文章将深入探讨Dubbo的核心特性、工作原理以及实际应用,帮助开发者全面理解并掌握这一强大工具。 1. **Dubbo简介** Dubbo是基于Java的RPC框架,旨在提高服务的透明性和可扩展性。它提供了一种服务化的解决...

    Java底层知识点、源码解读,技术栈相关原理知识点、工具解读最佳实践、功能点实战,问题排查,开发技巧等

    8. **IO与NIO**: Java IO提供基于流的输入输出操作,而NIO(非阻塞I/O)引入了通道和缓冲区,提升了高并发场景下的性能。 9. **集合框架**: 遍历HashMap、ArrayList、LinkedList、TreeSet等集合的实现原理,掌握...

    JAVA高级工程师2

    在“JAVA高级工程师2”这一主题中,我们将深入探讨Java编程语言在安全、多线程、图形用户界面(GUI)开发、游戏制作、网络设计以及虚拟机(VM)参数优化等多个核心领域的高级知识。虽然IO(输入/输出)部分涉及较少...

    Netty技术的全面解读

    这个全面解读将分为两个部分:《Netty权威指南》和《Netty In Action中文版--文字版》。 首先,让我们深入探讨《Netty权威指南》。这本书通常会涵盖Netty的基本概念,包括其设计理念、核心组件以及如何构建基于...

    java面试笔试题大汇总 ~很全面 -

    这份“Java面试笔试题大汇总”资料可能包含以上各个领域的经典题目和解析,通过系统学习和实践,可以帮助求职者全面提升Java技术水平,以应对各种面试挑战。CSDNBlog.htm和CSDNBlog_files两个文件可能包含了文章内容...

    Java网络高级编程源码人邮金勇华曲俊生

    书中可能详细解析了这些协议的工作原理,以及如何使用Java实现这些协议的客户端和服务器端。 5. **网络安全**:包括加密、认证和授权等,Java提供SSL/TLS支持,可以实现安全套接层的网络通信。此外,网络安全还包括...

    java超全面的面试总结

    Java作为世界上最受欢迎的编程语言之一,其面试题的深度和广度都相当广泛。这篇面试总结涵盖了Spring、MyBatis等关键框架的核心概念和技术,旨在帮助求职者充分准备技术面试。以下是一些重要的Java面试知识点: 1. ...

    Java教程文档

    这本书可能深入探讨Java的高级特性和技术,例如反射、注解、垃圾收集机制、内存管理、JVM(Java虚拟机)工作原理,以及性能优化策略。可能还会涵盖Java并发编程,包括线程池、同步机制、锁和并发容器的使用。此外,...

    Java2参考大全(java第四版)

    8. **Java虚拟机(JVM)**:探讨了JVM的工作原理,包括内存管理、垃圾收集以及性能优化,有助于开发者写出更高效的代码。 9. **泛型**:详细解读了Java泛型的使用,包括类型擦除、通配符和边界,提高了代码的类型...

    Java开发典型模块大全(仅含程序源码)-20个Java项目

    8. **Web开发**:例如Servlet、JSP、Filter、Listener,以及MVC模式的实践,有助于理解Web应用程序的工作原理。 9. **GUI编程**:如Swing或JavaFX,用于创建桌面应用程序,理解事件处理、布局管理等。 10. **异常...

    java 面试 百度入职老哥整理 全是干货

    - Java中的网络IO模型,包括BIO、NIO和AIO的原理与区别。 6. Java安全 - Java中的安全加密相关知识点,包括了数据加密和安全通信的机制。 7. 操作系统和Linux相关知识 - 操作系统中进程、线程、同步机制等基础...

    毕向东java基础ppt与源代码

    1. **Java概述**(01-Java概述.pdf):这部分内容主要介绍了Java语言的发展历程、特点和应用领域,包括Java的跨平台特性(Write Once, Run Anywhere),解释器和JVM(Java虚拟机)的工作原理,以及如何安装和配置...

    java论文参考资料(供参考)

    附录中的英文翻译提供了对原理解读的国际视角,帮助读者更好地理解和应用这些知识。 1. **Java语言基础**: - 类与对象:Java是基于面向对象编程(OOP)的语言,其基本单位是类,通过类创建对象来实现数据和功能的...

    一些JAVA的基础教程书籍

    这篇教程集合将为你提供Java基础知识的全面学习,特别是针对网络应用程序开发的部分。以下是对这些书籍资源的详细解读: 1. **SL314_OH_GB.pdf** - 这个文件名可能代表"Oracle Help for Java Developers"的某个版本...

    200+最常见Java面试题参考答案(嗯嗯).pdf

    为了能够帮助求职者更好地准备面试,这类面试题集和答案通常会涵盖Java的基础知识点、面向对象、集合框架、异常处理、多线程编程、JVM知识、IO/NIO、网络编程、数据库连接、设计模式、框架使用等重要部分。...

    JAVA程序员面试宝典 第4版-欧立奇

    - **I/O流与NIO**:I/O流是JAVA进行文件读写的基础,NIO则是一种新的输入输出方式。本章将对比这两种技术,并分析它们的优缺点。 - **JVM内存模型**:深入剖析JVM的内存布局,包括堆内存、栈内存等区域的作用及管理...

    java程序员的成长历程

    以下就是一篇关于“Java程序员的成长历程”的详细解读。 首先,Java初学者通常会从学习基础语法开始,包括变量、数据类型、控制结构(如if语句和循环)、类与对象的概念。理解这些基础知识是构建扎实编程技能的第一...

    java面试大全

    2. **集合框架**:Java集合框架是面试中的热门话题,包括List(ArrayList、LinkedList)、Set(HashSet、TreeSet)和Map(HashMap、LinkedHashMap、TreeMap)等接口及其实现类的使用、性能分析和源码解读。...

    Java核心技术 卷II 高级特性 原书第9版

    6. **垃圾收集与内存管理**:Java的自动内存管理机制,包括垃圾收集器的工作原理和内存分区,以及如何避免内存泄漏和提高应用性能。 7. **泛型**:泛型是Java 5引入的新特性,用于在编译时增强类型安全性,减少类型...

    【Java面试资料】-(机构内训资料)深圳-OPPO-Java高级

    8. **JVM内存模型**:理解堆内存、栈内存、方法区、本地方法栈的工作原理,以及垃圾回收(GC)机制。 9. **Spring框架**:包括依赖注入(DI)、AOP、事务管理、Spring Boot和Spring Cloud等相关知识,这些在企业级...

Global site tag (gtag.js) - Google Analytics