Java NIO1:概述

前言

相比I/O,NIO更复杂、更不好理解,因此在开始NIO之前,需要讲解一些概念,如果对于这些概念有着良好的理解,对于学习NIO绝对是有好处的。

同步与异步

所谓同步就是指一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能完成,这是一种可靠的任务序列。要成功都成功,要失败都失败,两个任务的状态可以保持一致。

而异步不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。

在涉及I/O处理时通常都会遇到是同步还是异步的处理方式的选择问题,因为同步与异步的I/O处理方式对调用者的影响很大,在数据库产品中都会遇到这个问题,因为I/O操作通常是一个非常耗时的操作,在一个任务序列中I/O通常都是性能瓶颈。但是同步与异步的处理方式对程序的可靠性影响非常大,同步能够保证程序的可靠性,而异步可以提升程序的性能,必须在可靠性和性能之间保持平衡,却没有完美的解决办法。

阻塞与非阻塞

阻塞与非阻塞是从CPU的消耗上来说的。

阻塞就是CPU停下来等待一个慢的操作完成以后,CPU才接着完成其他的工作,在执行这个慢的操作的过程中CPU需要不断轮询操作是否完成。

非阻塞就是在这个慢的操作执行时,CPU去做其他工作,等这个慢的操作完成了,CPU再接着完成后续的操作。

虽然从表面上看非阻塞的方式可以明显地提高CPU的利用率,但是也带来了另外一种后果,就是系统的线程切换的增加,增加的CPU使用时间能不能补偿系统的切换成本需要好好评估。

CPU已不再是束缚

Java程序员把全部精力用在优化处理效率上,而对I/O关注不足,在某种程度上并非他们的错。在Java早期,JVM在解释字节码时往往很少或没有运行时优化,这就意味着,Java程序往往拖得很长,其运行效率大大低于本地编译代码,因而对操作系统I/O子系统的要求不太高。

如今在运行时优化方面,JVM已经前进了一大步,现在JVM运行字节码的速率已经接近本地编译代码,借助动态运行时优化,其表现甚至还有所超越。这就意味着,多数Java应用程序已经不再受CPU的束缚(把大量时间用在执行代码上),而更多时候是受I/O的束缚(等待数据传输)。

然而,在大多数情况下,Java应用程序并非真的受着I/O的束缚。操作系统并非无法快速传送数据,让Java有事可做,而是JVM自身在 I/O方面效率欠佳。操作系统与Java基于流的I/O模式有些不匹配。操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取(DMA)的协助下完成的。而JVM的I/O类喜欢操作小块数据----单个字节、几行文本。结果,操作系统送来整块缓冲区的数据,java.io的流类再花大量时间把它们拆成小块,往往拷贝一个小块就要往返于几层对象。

有了NIO,就可以轻松把大量数据传输到需要直接使用的地方。不过,这并不是说使用传统的I/O模型无法移动大量数据----当然可以,RandomAccesssFile类就可以,而且效率也不差,只要坚持使用基于数组的read()、write()方法。这些方法与底层操作系统调用相当接近,尽管必须保留至少一份缓冲区拷贝。

NIO的提出

JDK1.4的NIO软件包引入了一套新的抽象用于I/O处理。与以往不同的是,新的抽象把重点放在了如何缩短抽象与现实之间的距离上面。NIO抽象与现实中存在的实体有着非常真实直接的交互关系。要想最大限度地满足Java应用程序密集I/O需求,理解这些新的抽象,以及与其发生交互作用的I/O服务,正是关键所在。

在NIO的学习中,理解以下概念是非常重要的:

  • 缓冲区操作
  • 内核空间与用户空间
  • 虚拟内存
  • 分页技术
  • 面向文件的I/O与流I/O
  • 多工I/O

缓冲区操作

缓冲区,以及缓冲区如何工作,是所有I/O的基础。所谓"输入/输出"讲的无非就是把数据移入或移出缓冲区。

执行I/O操作,归结起来,也就是向操作系统发出请求,让它要么把缓冲区里的数据读走,要么用数据把缓冲区填满。进程使用这一机制处理所有数据进出操作。操作系统内部处理这一任务的机制,其复杂程序可能超乎想象,但就概念而言,非常简单易懂。

下图描述了数据从外部磁盘向运行中的进程的内存区域移动的过程:

进程使用read()系统调用,要求其缓冲区被填满。内核随机向磁盘控制硬件发出命令,要求其从磁盘读取数据。磁盘控制器把数据写入内核内存缓冲区,这一步通过DMA完成,无需主CPU协助。一旦磁盘控制器把缓冲区填满,内核即把数据从内核空间的临时缓冲区拷贝到进程read()调用时指定的缓冲区。

注意用户空间和内核空间的概念。用户空间是常规进程所在的区域,JVM就是常规进程,驻守于用户空间。用户空间是非特权区域:比如在该区域执行的代码就不能直接访问硬件设备。内核空间是操作系统所在的区域。内核代码有特别的权利:它能与设备控制器通讯,控制着用户区域进程的运行状态等。最重要的是,所有I/O都直接或间接通过内核空间。

当进程请求I/O操作的时候,它执行一个系统调用将控制权移交给内核。C/C++程序员熟知的底层函数open()、read()、write()、close()要做的无非就是建立和执行适当的系统调用。党内和以这种方式被盗用,它随即采取任何必要步骤,找到进程所需数据,并把数据传送到用户空间内的指定缓冲区。内核试图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了。如果是这样,改数据只需要简单拷贝出来即可。如果数据不在内核空间,则进程被挂起,内核着手把数据读进内存。

可能有人会觉得,为什么不直接让磁盘控制器把数据传送到用户空间的缓冲区呢?这样做有几个问题:

1、硬件通常无法直接访问用户空间

2、像磁盘这样基于块存储的硬件设备操作的是固定大小的数据块,而用户进程请求的可能是任意大小的或非对其的数据块。在数据往来于用户空间和存储设备的过程中,内核负责数据的分解、再组合工作

虚拟内存

所有现代操作系统都使用虚拟内存。虚拟内存意为使用虚假(或虚拟)地址取代物理(硬件RAM)内存地址。这样做好处颇多,总结起来可以分为:

1、一个以上的虚拟地址可指向同一个物理内存地址

2、虚拟内存空间可大于实际可用的硬件内存

前面提到,控制设备器不能通过DMA直接存储到用户空间,但通过利用上面提到的第一项,则可以达到相同效果。把内核空间地址与用户空间的虚拟地址映射到同一个物理地址,这样,DMA硬件(只能访问物理内存地址)就可以填充对内核与用户空间进程同时可见的缓冲区了:

这样真的太好了,省去了内核空间与用户空间的往来拷贝,但前提是,内核与用户缓冲区必须使用相同的页对齐,缓冲区的大小还必须是磁盘控制器块大小的倍数。

时间: 2024-08-06 22:54:36

Java NIO1:概述的相关文章

Java IO 概述

原文链接作者: Jakob Jenkov   译者: 李璟([email protected])  校对:方腾飞 在这一小节,我会试着给出Java IO(java.io)包下所有类的概述.更具体地说,我会根据类的用途对类进行分组.这个分组将会使你在未来的工作中,进行类的用途判定时,或者是为某个特定用途选择类时变得更加容易. 输入和输出 – 数据源和目标媒介 术语“输入”和“输出”有时候会有一点让人疑惑.一个应用程序的输入往往是另外一个应用程序的输出.那么OutputStream流到底是一个输出到

[JAVA_开课吧资源]第一周 Java语言概述、Java语言基础

主题一 Java语言概述 » JDK介绍及其基本组件 Sun公司利用Java开发工具箱(Java Development Toolkit ,JDK)发布Java的各个版本.JDK由开发和测试Java程序的独立程序集组成,它们都从命令行调用. [请点击查看更多内容 转自51CTO.com] » 使用CMD开发运行Java程序 代码文件的拓展名必须为.java,并且文件名应该与类的类名完全相同.编译一个程序是指使用编译器将源代码翻译成java字节码.下面的命令用来编译源代码文件welcome.jav

跟王老师学集合(一)为什么要使用集合和Java集合概述

为什么要使用集合和Java集合概述 主讲人:王少华  QQ群号:483773664 学习目标 1 理解为什么使用集合 2 掌握Java集合框架 一.使用数组缺陷: 在电子宠物系统中,如果想存储多个宠物信息,可以使用数组来实现.例如,可以定义一个长度50的Dog类型的数组,存储多个Dog对象的信息.但是采用数组存在以下一些明显的缺陷: 数组长度固定不变,不能很好适应元素数量动态变化的情况.若要存储大于50个狗狗的信息,则数组长度不足:若只存储20个狗狗的信息,则造成内存空间浪费 可通过数组名.le

Java的概述以及语法

Java的语法分为标示符和数据类型 Java的概述: 一些手打的: long l = 12345; //隐式转换 int a = (int)121234567L; //强制转换 float f =123.45;(提示报错) 默认为int类型,所以无法转换 下面三种为正确格式: { float f = 123.45F; f = (float)123.45; double d =123.45; } char c =‘中’; char和int是兼容型 char c1 =123; System.out.

java 语言概述(零)

java 语言概述 1.软件:系统软件 vs 应用软件 2.语言的分类:第一代:机器语言 第二代:汇编语言 第三代:高级语言(面向过程- - - -面向对象) 3.java语言的特性:①面对对象性②健壮性③跨平台性(write  once,run  anywhere) - - -JVM 4.安装JDK及配置path环境变量 1)傻瓜式安装JDK 2)配置path环境变量 >河床好比操作底层,jdk好比河水,java应用程序好比是船   注意:JDK  JRE  JVM   5.如何编写并运行第一

java接口概述

接口: 是一些方法特征的集合,而这些方法可以被多个实现类去各自实现. 接口主要特征: 1.接口中的成员变量隐士的被定义为公开的静态常量 2.接口中的方法是抽象的,并且必须是公开的 3.接口是用来被类实现的或者被接口继承的,其不能有自己的构造子 接口的作用: 接口是实现松耦合的重要手段,它描述了系统对外公开的所有服务.通俗的理解,接口的作用就是把使用接口的人和提供接口的 人隔离开来,让真正的接口放在他们中间,实现接口的人不需要去关心谁使用这个接口,使用接口的人也不需要关心这个接口是 谁实现的.就好

《java集合概述》

1 JAVA集合概述: 2 3 Collection: 4 |---List有序的:通过索引就可以精确的操作集合中的元素.元素是可以重复的. 5 List提供了增删改查的动作. 6 增加add(element) add(index,element); 7 删除remove(element) remove(index); 8 修改set(index,element); 9 查询get(index); 10 |---Vector:是可增长的数组结构.是同步的.增和查速度都慢,效率很低.目前不在使用.

Java继承概述 以及Java继承案例和继承的好处

Java继承概述 1.多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些相同属性和行为,只要继承那个类即可. 2.在Java中通过extends关键字可以实现类与类的继承. 例如:class 子类名 extends 父类名 {} 3.单独的这个类称为父类,基类或者超类:这多个类可以称为子类或者派生类. 4.有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员. --------------------------------

Java多线程-Java多线程概述

第一章 Java多线程概述 线程的启动 线程的暂停 线程的优先级 线程安全相关问题 1.1 进程与线程 进程:可以将运行在内存中的程序(如exe文件)理解为进程,进程是受操作系统管理的基本的运行单元. 线程:可以理解为进程中独立运行的子任务.如果QQ.exe运行时的好友视频线程.下载文件线程.数据传输线程.发送消息线程等. 使用多线程可以更好的利用计算机的资源如CPU.线程被调用的时机是随机的. 1.2 Java多线程实现方式 1.2.1 继承Thread类 public class Threa

java的概述 常量 变量

1.1 java语言概述 什么是java语言 Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个特征.Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程. Java具有简单性.面向对象.分布式.健壮性.安全性.平台独立与可移植性.多线程.动态性等特点. Java可以编写桌面应用程序.Web应用程序.分布式系统和嵌入式系统应用程序等. -