java nio 入门教程详解(1)

上传人:ji****en 文档编号:107694147 上传时间:2019-10-20 格式:PDF 页数:28 大小:466.80KB
返回 下载 相关 举报
java nio 入门教程详解(1)_第1页
第1页 / 共28页
java nio 入门教程详解(1)_第2页
第2页 / 共28页
java nio 入门教程详解(1)_第3页
第3页 / 共28页
java nio 入门教程详解(1)_第4页
第4页 / 共28页
java nio 入门教程详解(1)_第5页
第5页 / 共28页
点击查看更多>>
资源描述

《java nio 入门教程详解(1)》由会员分享,可在线阅读,更多相关《java nio 入门教程详解(1)(28页珍藏版)》请在金锄头文库上搜索。

1、Java nio 入门教程详解(一) 1.1 I/O 与与 CPU 时间的比较时间的比较 程序员多半当自个儿是软件大师,设计出精巧的例程,这儿压缩几个字节,那儿解开一个循环,要不就在 别处作些调整,让对象更加牢固。这些事情当然很重要,乐趣也不少,但是代码优化所带来的回报,可能 轻易就被低效的 I/O 所抵销。 I/O 操作比在内存中进行数据处理任务所需时间更长, 差别要以数量级计。 许多程序员一门心思扑在他们的对象如何加工数据上,对影响数据取得和存储的环境问题却不屑一顾。 表 1-1 所示为对数据单元进行磁盘读写所需时间的假设值。第一列为处理一个数据单元所需平均时间, 第二列为对该数据单元进行

2、磁盘读写所需时间,第三列为每秒所能处理的数据单元数,第四列为改变第一 第二列的值所能产生的数据吞吐率的提升值。 表表 1-1. 处理时间与处理时间与 I/O 时间对吞吐率的影响比较时间对吞吐率的影响比较 处理时间(ms)I/O 时间(ms)吞吐率(units/sec)增益(%) 51001000/(5+100)=9.52(基准) 2.51001000/(2.5+100)=9.76 2.44 11001000/(1+100)=9.93.96 5901000/(5+90)=10.5310.53 5751000/(5+75)=12.531.25 5501000/(5+50)=18.1890.91 5

3、201000/(5+20)=40320 5101000/(5+10)=66.67600 前三行显示了处理阶段的效率提升会如何影响吞吐率。把单位处理时间减半,仅能提高吞吐率 2.2。而 另一方面,仅仅缩短 I/O 延迟 10,就可使吞吐率增加 9.7;把 I/O 时间减半,吞吐率几乎翻番。 当您了解到 I/O 花在一个数据单元上的时间是处理时间的 20 倍,这样的结果就不足为奇了。 表中所列并非真实数据,目的只在说明相对时间度量,现实情况绝非如此简单。正如您所看到的,影响应 用程序执行效率的限定性因素,往往并非处理速率,而是 I/O。程序员热衷于调试代码,I/O 性能的调试 往往被摆在第二位,甚

4、至完全忽略。殊不知,在 I/O 性能上的小小投入就可换来可观的回报,想来实在 令人惋惜。 1.2 CPU 已不再是束缚已不再是束缚 Java 程序员把全部精力用在优化处理效率上,而对 I/O 关注不足,在某种程度上讲这并非他们的错。在 Java 的早期,JVM 在解释字节码时往往很少或没有运行时优化。这就意味着,Java 程序往往拖得很长, 其运行速率大大低于本地编译代码,因而对操作系统 I/O 子系统的要求并不太高。如今在运行时优化方 面,JVM 已然前进了一大步。现在 JVM 运行字节码的速率已经接近本地编译代码,借助动态运行时优化, 其表现甚至还有所超越。这就意味着,多数 Java 应用

5、程序已不再受 CPU 的束缚(把大量时间用在执行 代码上),而更多时候是受 I/O 的束缚(等待数据传输)。 然而,在大多数情况下,Java 应用程序并非真的受着 I/O 的束缚。操作系统并非不能快速传送数据, 让 Java 有事可做;相反,是 JVM 自身在 I/O 方面效率欠佳。操作系统与 Java 基于流的 I/O 模型 有些不匹配。操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取(DMA)的协助 下完成的。而 JVM 的 I/O 操作类喜欢操作小块数据单个字节、几行文本。结果,操作系统送来整缓 冲区的数据,java.io 包的流数据类再花大量时间把它们拆成小块,往往拷

6、贝一个小块就要往返于几层 对象。操作系统喜欢整卡车地运来数据,java.io 类则喜欢一铲子一铲子地加工数据。有了 NIO,就可 以轻松地把一卡车数据备份到您能直接使用的地方(ByteBuffer 对象)。 这并不是说使用传统的 I/O 模型无法移动大量数据当然可以(现在依然可以)。具体地说, RandomAccessFile 类在这方面的效率就不低,只要坚持使用基于数组的 read()和 write()方法。 这些方法与底层操作系统调用相当接近,尽管必须保留至少一份缓冲区拷贝。如表 1-1 所示,如果您的 代码大部分时间都处于 I/O 等待状态,那么,该考虑一下提升 I/O 效率的问题了,否

7、则,您精心打造的 代码多数时间都得闲着。 Java nio 入门教程详解(二) Java 2013 年 8 月 7 日 暂无评论 1.3 进入正题进入正题 操作系统研发人员将大量精力投入到提升 I/O 性能上。众多高手日以继夜地工作,只为完善数据传输技 术。操作系统开发商为了取得竞争优势,投入大量时间、金钱,以便在测试数据上胜过竞争对手。 当今的操作系统是现代软件工程的奇迹(没错,有的比奇迹还奇迹),可是 Java 程序员如何能够既利用 操作系统的强大功能,又保持平台独立性?唉,天下没有免费的午餐,此为一例。JVM 是把双刃剑。它提 供了统一的操作环境,让 Java 程序员不用再为操作系统环境

8、的区别而烦恼。与特定平台相关的细枝末节 大都被隐藏了起来,因而代码写得又快又容易。但是隐藏操作系统的技术细节也意味着某些个性鲜明、功 能强大的特性被挡在了门外。 怎么办呢?如果您是程序员,可以使用 Java 本地接口(JNI)编写本地代码,直接使用操作系统特性。 这样的话,您就被绑定在该操作系统上(也许还是其特定版本上)。如果您的本地代码不是 100%无漏洞, 您还可能把 JVM 置于频繁出错乃至崩溃的境地。 如果您是操作系统开发商, 则可以在您的 JVM 实现中包 含本地代码,以 Java API 的形式提供这些特性。但这样做可能违反您所签署相关许可协议,根据协议, 您只能提供符合一致性要求

9、的 JVM。 Sun 曾就此问题将 Microsoft 告上法庭, 因为很明显, JDirect 软 件包只能在微软的系统上运行。如果以上方法都行不通,那么您只好转向其他语言,以实现对性能要求极 为苛刻的应用。 为了解决这一问题,java.nio 软件包提供了新的抽象。具体地说,就是 Channel 和 Selector 类。它们 提供了使用 I/O 服务的通用 API,JDK 1.4 以前的版本是无法使用这些服务的。天下还是没有免费的 午餐:您无法使用每一种操作系统的每一种特性,但是这些新类还是提供了强大的新框架,涵盖了当今商 业操作系统普遍提供的高效 I/O 特性。 不仅如此, java.

10、nio.channels.spi 还提供了新的服务提供接口 (SPI),允许接入新型通道和选择器,同时又不违反规范的一致性。 随着 NIO 的面世, Java 已经为严肃的商业、 娱乐、 科研和学术应用做好了准备。 在这些领域, 高性能 I/O 是必不可少的。 除了 NIO,JDK 1.4 还包含许多其他重要改进。从 1.4 版开始,Java 平台已进入高度成熟期,它仍 无法涉足的应用领域已所剩无几。David Flanagan 所著Java 技术手册(第四版)(Java ina Nutshell, Fourth Edition OReilly)是全面了解 JDK 1.4 各方面特性的绝佳向导

11、。 Java nio 入门教程详解(三) Java 2013 年 8 月 7 日 暂无评论 1.4 I/O 概念概念 Java 平台提供了一整套 I/O 隐喻,其抽象程度各有不同。然而,离冰冷的现实越远,要想搞清楚来龙去 脉就越难不管使用哪一种抽象,情况都是如此。JDK 1.4 的 NIO 软件包引入了一套新的抽象用于 I/O 处理。与以往不同的是,新的抽象把重点放在了如何缩短抽象与现实之间的距离上面。NIO 抽象与现实中 存在的实体有着非常真实直接的交互关系。要想最大限度地满足 Java 应用程序的密集 I/O 需求,理解 这些新的抽象,以及与其发生交互作用的 I/O 服务(其重要性并不亚于

12、抽象),正是关键所在。 这里假定您熟知基本的 I/O 概念,因此,本节将快速回顾一些基本概念,为下一步论述新的 NIO 类如何 运作奠定基础。NIO 类模拟 I/O 函数,因此,必须掌握操作系统层面的处理细节,才能理解新的 I/O 模 型。 在阅读的过程中,理解以下概念是非常重要的: 缓冲区操作 内核空间与用户空间 虚拟内存 分页技术 面向文件的 I/O 和流 I/O 多工 I/O(就绪性选择) 1.4.1 缓冲区操作 缓冲区,以及缓冲区如何工作,是所有 I/O 的基础。所谓“输入输出”讲的无非就是把数据移进或移出 缓冲区。 进程执行 I/O 操作,归结起来,也就是向操作系统发出请求,让它要么

13、把缓冲区里的数据排干(写),要 么用数据把缓冲区填满(读)。进程使用这一机制处理所有数据进出操作。操作系统内部处理这一任务的机 制,其复杂程度可能超乎想像,但就概念而言,却非常直白易懂。图 1-1 简单描述了数据从外部磁盘向 运行中的进程的内存区域移动的过程。进程使用 read()系统调用,要求其缓冲区被填满。内核随即向磁盘 控制硬件发出命令, 要求其从磁盘读取数据。 磁盘控制器把数据直接写入内核内存缓冲区, 这一步通过 DMA 完成,无需主 CPU 协助。一旦磁盘控制器把缓冲区装满,内核即把数据从内核空间的临时缓冲区拷贝到进 程执行 read()调用时指定的缓冲区。 图 1-1. I/O 缓

14、冲区操作简图 图中明显忽略了很多细节,仅显示了涉及到的基本步骤。 注意图中用户空间和内核空间的概念。用户空间是常规进程所在区域。JVM 就是常规进程,驻守于用户空 间。用户空间是非特权区域:比如,在该区域执行的代码就不能直接访问硬件设备。内核空间是操作系统 所在区域。内核代码有特别的权力:它能与设备控制器通讯,控制着用户区域进程的运行状态,等等。最 重要的是,所有 I/O 都直接(如这里所述)或间接(见 1.4.2 小节)通过内核空间。当进程请求 I/O 操 作的时候,它执行一个系统调用(有时称为陷阱)将控制权移交给内核。C/C+程序员所熟知的底层函数 open()、read()、write(

15、)和 close()要做的无非就是建立和执行适当的系统调用。当内核以这种方式被 调用,它随即采取任何必要步骤,找到进程所需数据,并把数据传送到用户空间内的指定缓冲区。内核试 图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了。如果是这样,该数据只需 简单地拷贝出来即可。如果数据不在内核空间,则进程被挂起,内核着手把数据读进内存。 看了图 1-1,您可能会觉得,把数据从内核空间拷贝到用户空间似乎有些多余。为什么不直接让磁盘控制 器把数据送到用户空间的缓冲区呢?这样做有几个问题。首先,硬件通常不能直接访问用户空间。其次, 像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而

16、用户进程请求的可能是任意大小的或非 对齐的数据块。在数据往来于用户空间与存储设备的过程中,内核负责数据的分解、再组合工作,因此充 当着中间人的角色。 1.4.1.1 发散汇聚 许多操作系统能把组装分解过程进行得更加高效。根据发散汇聚的概念,进程只需一个系统调用,就 能把一连串缓冲区地址传递给操作系统。然后,内核就可以顺序填充或排干多个缓冲区,读的时候就把数 据发散到多个用户空间缓冲区,写的时候再从多个缓冲区把数据汇聚起来(图 1-2)。 图 1-2. 三个缓冲区的发散读操作 这样用户进程就不必多次执行系统调用(那样做可能代价不菲),内核也可以优化数据的处理过程,因为它 已掌握待传输数据的全部信息。如果系统配有多个 CPU,甚至可以同时填充或排干多个缓冲区。 1.4.2 虚拟内存 所有现代操作系统都使用虚拟内存。虚拟内存意为使用虚假(或虚拟)地址取代物理(硬件 RAM)内存地址。 这样做好处颇多,总结起来可分为两大类: 1. 一个以上的虚拟地址可指向同一个物理内存地址。 2. 虚拟内存空间可大于实际可用的硬件内存。 前一节提到,设备控制器

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 电子/通信 > 综合/其它

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号