`

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

    博客分类:
  • Java
阅读更多

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

Sky:

 

◆  输入/输出:概念性描述

I/O 简介

I/O ? 或者输入/输出 ? 指的是计算机与外部世界或者一个程序与计算机的其余部分的之间的接口。它对于任何计算机系统都非常关键,因而所有 I/O 的主体实际上是内置在操作系统中的。单独的程序一般是让系统为它们完成大部分的工作。

在 Java 编程中,直到最近一直使用 流 的方式完成 I/O。所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节。流 I/O 用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对象。

NIO 与原来的 I/O 有同样的作用和目的,但是它使用不同的方式? 块 I/O。正如您将在本教程中学到的,块 I/O 的效率可以比流 I/O 高许多。

为什么要使用 NIO?

NIO 的创建目的是为了让 Java 程序员可以实现高速 I/O 而无需编写自定义的本机代码。NIO 将最耗时的 I/O 操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。

流与块的比较

原来的 I/O 库(在 java.io.*中) 与 NIO 最重要的区别是数据打包和传输的方式。正如前面提到的,原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。

面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。

一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。

集成的 I/O

在 JDK 1.4 中原来的 I/O 包和 NIO 已经很好地集成了。 java.io.* 已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如, java.io.* 包中的一些类包含以块的形式读写数据的方法,这使得即使在更面向流的系统中,处理速度也会更快。

也可以用 NIO 库实现标准 I/O 功能。例如,可以容易地使用块 I/O 一次一个字节地移动数据。但是正如您会看到的,NIO 还提供了原 I/O 包中所没有的许多好处。

◆ 通道和缓冲区

概  述

通道 和 缓冲区 是 NIO 中的核心对象,几乎在每一个 I/O 操作中都要使用它们。

通道是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象。一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。

在本节中,您会了解到 NIO 中通道和缓冲区是如何工作的。

什么是缓冲区?

Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。 在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中。

在 NIO 库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问 NIO 中的数据,您都是将它放到缓冲区中。

缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不 仅仅 是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。

缓冲区类型

最常用的缓冲区类型是 ByteBuffer。一个 ByteBuffer 可以在其底层字节数组上进行 get/set 操作(即字节的获取和设置)。

ByteBuffer 不是 NIO 中唯一的缓冲区类型。事实上,对于每一种基本 Java 类型都有一种缓冲区类型:

• ByteBuffer

• CharBuffer

• ShortBuffer

• IntBuffer

• LongBuffer

• FloatBuffer

• DoubleBuffer

每一个 Buffer 类都是 Buffer 接口的一个实例。 除了 ByteBuffer,每一个 Buffer 类都有完全一样的操作,只是它们所处理的数据类型不一样。因为大多数标准 I/O 操作都使用 ByteBuffer,所以它具有所有共享的缓冲区操作以及一些特有的操作。

现在您可以花一点时间运行 UseFloatBuffer.java,它包含了类型化的缓冲区的一个应用例子。

什么是通道?

Channel是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流。

正如前面提到的,所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

通道类型

通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类), 而 通道 可以用于读、写或者同时用于读写。

因为它们是双向的,所以通道可以比流更好地反映底层操作系统的真实情况。特别是在 UNIX 模型中,底层操作系统通道是双向的。

◆ 从理论到实践:NIO 中的读和写

概  述

读和写是 I/O 的基本过程。从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中。写入也相当简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作。

在本节中,我们将学习有关在 Java 程序中读取和写入数据的一些知识。我们将回顾 NIO 的主要组件(缓冲区、通道和一些相关的方法),看看它们是如何交互以进行读写的。在接下来的几节中,我们将更详细地分析这其中的每个组件以及其交互。

从文件中读取

在我们第一个练习中,我们将从一个文件中读取一些数据。如果使用原来的 I/O,那么我们只需创建一个 FileInputStream 并从它那里读取。而在 NIO 中,情况稍有不同:我们首先从 FileInputStream 获取一个 FileInputStream 对象,然后使用这个通道来读取数据。

在 NIO 系统中,任何时候执行一个读操作,您都是从通道中读取,但是您不是 直接 从通道读取。因为所有数据最终都驻留在缓冲区中,所以您是从通道读到缓冲区中。

因此读取文件涉及三个步骤:(1) 从 FileInputStream 获取 Channel,(2) 创建 Buffer,(3) 将数据从 Channel 读到 Buffer 中。

现在,让我们看一下这个过程。

三个容易的步骤

第一步是获取通道。我们从 FileInputStream 获取通道:

  1. FileInputStream fin = new FileInputStream( "readandshow.txt" );  
  2. FileChannel fc = fin.getChannel(); 

下一步是创建缓冲区:

  1. ByteBuffer buffer = ByteBuffer.allocate( 1024 ); 

最后,需要将数据从通道读到缓冲区中,如下所示:

  1. fc.read( buffer ); 

您会注意到,我们不需要告诉通道要读 多少数据 到缓冲区中。每一个缓冲区都有复杂的内部统计机制,它会跟踪已经读了多少数据以及还有多少空间可以容纳更多的数据

写入文件

在 NIO 中写入文件类似于从文件中读取。首先从 FileOutputStream 获取一个通道:

  1. FileOutputStream fout = new FileOutputStream( "writesomebytes.txt" );  
  2. FileChannel fc = fout.getChannel(); 

下一步是创建一个缓冲区并在其中放入一些数据 - 在这里,数据将从一个名为 message 的数组中取出,这个数组包含字符串 "Some bytes" 的 ASCII 字节(本教程后面将会解释 buffer.flip() 和 buffer.put() 调用)。

  1. ByteBuffer buffer = ByteBuffer.allocate( 1024 );  
  2.  for (int i=0; i<message.length; ++i) {  
  3.      buffer.put( message[i] );  
  4. }  
  5. buffer.flip(); 

最后一步是写入缓冲区中

  1. fc.write( buffer ); 

注意在这里同样不需要告诉通道要写入多数据。缓冲区的内部统计机制会跟踪它包含多少数据以及还有多少数据要写入。

读写结合

下面我们将看一下在结合读和写时会有什么情况。我们以一个名为 CopyFile.java 的简单程序作为这个练习的基础,它将一个文件的所有内容拷贝到另一个文件中。CopyFile.java 执行三个基本操作:首先创建一个 Buffer,然后从源文件中将数据读到这个缓冲区中,然后将缓冲区写入目标文件。这个程序不断重复 ― 读、写、读、写 ― 直到源文件结束。

CopyFile 程序让您看到我们如何检查操作的状态,以及如何使用 clear() 和 flip() 方法重设缓冲区,并准备缓冲区以便将新读取的数据写到另一个通道中。

运行 CopyFile 例子

因为缓冲区会跟踪它自己的数据,所以 CopyFile 程序的内部循环 (inner loop) 非常简单,如下所示:

  1. fcin.read( buffer );  
  2. fcout.write( buffer ); 

第一行将数据从输入通道 fcin 中读入缓冲区,第二行将这些数据写到输出通道 fcout 。

检查状态

下一步是检查拷贝何时完成。当没有更多的数据时,拷贝就算完成,并且可以在 read() 方法返回 -1 是判断这一点,如下所示:

  1. int r = fcin.read( buffer );  
  2.  if (r==-1) {  
  3.      break;  

重设缓冲区

最后,在从输入通道读入缓冲区之前,我们调用 clear() 方法。同样,在将缓冲区写入输出通道之前,我们调用 flip() 方法,如下所示

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

clear() 方法重设缓冲区,使它可以接受读入的数据。 flip() 方法让缓冲区可以将新读入的数据写入另一个通道。

 

分享到:
评论

相关推荐

    dubbo最新全面深度解读

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

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

    1. **Java虚拟机(JVM)**: JVM是Java程序运行的基础,它负责字节码的解释执行,内存管理(如垃圾回收),以及类加载机制。理解JVM的工作原理有助于优化程序性能,例如通过调整JVM参数来控制堆内存大小,设置垃圾...

    Netty技术的全面解读

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

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

    1. **Java基础语法**:这是Java学习的起点,包括变量、数据类型、运算符、流程控制(if、switch、for、while)、方法定义与调用、类和对象等。理解这些基础知识是成为一名合格Java程序员的必备条件。 2. **面向对象...

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

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

    java超全面的面试总结

    1. **基础语法**: - 数据类型:Java分为基本类型和引用类型,理解它们的区别和用途。 - 控制流:包括if-else,switch,for,while等语句的使用。 - 类与对象:理解面向对象编程的基本概念,如封装、继承和多态。...

    JAVA高级工程师2

    虽然IO部分在此主题中提及较少,但作为全面的Java开发者,对于IO的理解同样重要,只是在本课程中可能不是重点。通过深入学习这些内容,开发者将能够提升自己的技术水平,更好地应对各种复杂项目的需求。

    Java教程文档

    这本书可能深入探讨Java的高级特性和技术,例如反射、注解、垃圾收集机制、内存管理、JVM(Java虚拟机)工作原理,以及性能优化策略。可能还会涵盖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的基础教程书籍

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

    毕向东java基础ppt与源代码

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

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

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

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

    1. Java基础知识点:包括Java语言的基本语法、数据类型、运算符、控制流等。 2. 面向对象编程:涵盖类和对象的概念、继承、封装、多态、接口和抽象类等概念。 3. 集合框架:讨论了Collection和Map接口、各种集合类...

    Java2参考大全(java第四版)

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

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

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

    java程序员的成长历程

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

    java面试大全

    3. **多线程与并发编程**:Java提供了丰富的并发工具类,如synchronized、volatile、ThreadLocal、Lock、Future等,理解其工作原理和使用场景对于编写高效、安全的并发代码至关重要。 4. **JVM**:面试中常常会问到...

    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