`
cynhafa
  • 浏览: 153057 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

调用Java NIO提高文件读写速度(1)

 
阅读更多

http://developer.51cto.com/art/201111/302489_1.htm

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

AD:


    Java NIO的出现旨在提高文件的读写速度,当然IO用NIO重新实过,所以我们不用显示的调用NIO也能享受这种高效的文件读写。

    Java NIO的高效得益于其两大"助手":Channel(管道)和Buffer(缓冲器)。当然这两个"得力助手"的"年龄"远远比java大!力求简单易懂的把知识讲解给大家,我举一个例子来说明一下这"两元大将"是如何在java NIO中配合工作的。

    中国古代有一种传统的吸烟器具---水烟袋。我想用这个东西来模拟一下Channel和Buffer的工作原理。不求说的好,力求准确无误。

    分析一下水烟袋是如何工作的:

    第一步,准备工作,准备好上等烟丝;第二步,将"水斗"中装入适量的水,烟仓中装满烟丝并插入水斗中,然后再将烟管插入水斗中;第三步,点燃烟丝并吸气。香烟从烟仓产生,经过水的过滤进入水上的空闲区。第四步,享受吸烟的快感.....从这个例子中我们提取出主要对象"烟",来分析一下它的运动轨迹。烟仓把烟生产出来,经过水的过滤飘到水上面的空闲区域,然后通过烟管进入人的体内。

    如果上面的过程大家理解了,明白了,那么java NIO你已经了解了50%,至少你已经知道它的工作原理了。因为用NIO处理的数据和用水烟袋中吸烟很相似。我们分析一下NIO的工作原理,非常简单。

    当然和吸烟一样我们首先必须有要用NIO来处理需求的欲望(这好比你想要吸烟了),比方说我想要将C盘下面的wk.txt文件进行备份,备份文件的名称为wk-bak.txt。类比刚刚吸烟的那个过程:

    步骤一:准备工作,确定文件的位置,并将程序不可直接操作的文件转换成字符流的形式(这一步和上边吸烟实例的第一步没有什么差别,只是进行一些简单的准备工作)。

    1. String inFile = "C:\\wk.txt";
    2. String outFile = "C:\\wk-bak.txt";
    3. FileInputStream inf = new FileInputStream(inFile);
    4. FileOutputStream outf = new FileOutputStream(outFile);
    5. ByteBuffer buffer = ByteBuffer.allocate(1024);

    步骤二:创建文件输入管道,和文件输出管道。(这一步与上边吸烟的第二部稍有差别,因为Channel和Buffer是在读写的时候才发生的"连接"动作)

    1. //准备文件读取的管道-->相当于烟仓和烟管FileChannel inFc = inf.getChannel();
    2. FileChannel outFc = outf.getChannel();
    3. Charset charSet = Charset.forName("utf-8");
    4. //进行编码解码-->相当于水斗中水的过滤作用
    5. CharsetDecoder decoder = charSet.newDecoder();
    6. CharsetEncoder encoder = charSet.newEncoder();

    步骤三:开始进行文件备份工作。

    1. while(true) {
    2. //准备向Buffer中写入数据-->相当于点燃烟丝,完事具备只欠东风
    3. buffer.clear();
    4. //进行字符编码 -->相当于水的过滤作用
    5. CharBuffer cb = decoder.decode(buffer);
    6. ByteBuffer bb = encoder.encode(cb);
    7. //数据经过编码以后暂存缓冲区-->相当于经过水过滤后的烟暂停在水斗中
    8. int t = inFc.read(bb);
    9. if(t == -1) {
    10. break;
    11. }
    12. bb.flip();
    13. //将字节码写入目标文件-->相当于烟已经进入到嘴里
    14. outFc.write(bb);
    15. }

    步骤四:检查文件是否备份成功。发现C盘下面多了一个wk-bak.txt的文件,内容与wk.txt一摸一样。接下来享受java带给你的快感....

    上面的例子估计大家已经理解的差不多了,当然如果深究也会有一些不太妥当的地方,但是不要较真,目的是学习NIO,并不是吸烟。如果感觉你可以了那么就请把上面的例子补充完整,运行一下,享受一下NIO的威武(当然字符编码并不是必须的,只是让这个例子显得完整一点)。

    好吧如果你理解了上面的东西,并且真正的补全了文件备份的小程序,那么就来进行稍微深入一点的学习吧。

    上文我提到了举吸烟的例子是有欠妥当的,其中之一就是Buffer的内部机制和"水斗"简单的过滤功能是不一样的。还有字符编码那一块也不是在Buffer内部实现的东西,decoder和encoder是针对Buffer的两个工具。那我们接下来分析一下Buffer内部机制到底不一样在哪里呢(主要分析常用的两个方法;clear(),flip())?

    来吧,打开Buffer的源码(摘取有用的部分):

    1. public abstract class Buffer {
    2. // Invariants: mark <= position <= limit <= capacity
    3. private int mark = -1;
    4. private int position = 0;
    5. private int limit;
    6. private int capacity;
    7. public final Buffer clear() {
    8. position = 0;
    9. limit = capacity;
    10. mark = -1;
    11. return this;
    12. }
    13. public final Buffer flip() {
    14. limit = position;
    15. position = 0;
    16. mark = -1;
    17. return this;
    18. }

     首先我们要明确一点,所谓的缓冲器仅仅是一个"多功能"的数组。可能在这个Buffer类中没有体现,但是如果我们打开ByteBuffer的源码会有byte[]的数组,打开CharBuffer的源码会有char[]的数组。因为Buffer是所有缓冲器的父类,所以他它不能预计会有多少种缓冲器,所以索性让"儿子"们自己实现去吧。

    既然知道了缓冲器是一个"多功能的数组",那么我们用画图的形式来分析一下上面Buffer的源码。

    假设我们定义了一个8个单位大的缓冲区,如上图(其实Buffer也就是这么一个东西)。首先告诉大家那三个重要的关于缓冲区状态的的属性:

    capacity:缓冲区的容量;

    limit:缓冲区还有多少数据能够取出或者缓冲区还有多少容量用于存放数据;

    position:相当于一个游标(cursor),记录我们从哪里开始写数据,从哪里开始读数据。

    刚还说到flip()和clear()是Buffer的两个重要的方法,因为它们两个方法决定了缓冲是否能正常的进行读写工作。

    当我们要想从缓冲区中写数据的时候必须先执行flip()方法,当我们要想从缓冲区中读数据时必须先执行clear()方法。

    第一次向Buffer中写入数据时,执行一次flip()方法以后,Buffer的结构变成了这样:position指向了第一个可以存取数据的0号位,limit和capacity同时指向最高位。

    假如第一次我们向Buffer中写入了3单位的数据,我们再次执行flip()方法则Buffer的结构会变成上图的所示。但是经过flip()的改造后position总是指向Buffer中第一个可用的位置。那么,未执行flip()方法以前position在哪里呢?很简单,指向最后一个数据的位置。

    当我们想要从Buffer中读取数据时,执行clear()方法,Buffer的内部结构变成了上图所示,position指向了可读数据的首位,limit指向了原来position的位置。

    从上面的几幅图中我们看出:capacity代表了Buffer的容量是不变的,limit与position的差总是表示Buffer总可以读的数据,或者Buffer中可以写数据的容量。还有position总是小于等于limit,limit总是小于等于capacity。

    其实到这里我们已经发现,NIO并不像IO那么复杂,因为IO 中的Decorator模式和Adaptor模式确实让我们一时间摸不到头脑,但是熟悉了会感觉到IO的设计之精美。

    NIO中还有一个知识点就是无阻塞的Socket编程,这里就不说了,因为比较复杂,但是如果我们真正理解了Selector这个调度者的工作,那么无阻塞的实现机制我们差不多就掌握了,复杂也就是编码上面的事了。

    原文链接:http://www.cnblogs.com/focusj/archive/2011/11/03/2231583.html

    分享到:
    评论

    相关推荐

      java常用代码

      6.NioFile.java nio操作文件读写 7.ImageThumbnail.java 生成图片缩略图 8.JsonObjs.java json简单操作 9.HttpProxy.java 代理设置 10.CaptureScreen.java 截取桌面 11.XmlReaer.java 读写xml文件 12.ArrayConvers...

      JAVA上百实例源码以及开源项目

      2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户...

      java开源包1

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      JAVA上百实例源码以及开源项目源代码

      2个目标文件,FTP的目标是:(1)提高文件的共享性(计算机程序和/或数据),(2)鼓励间接地(通过程序)使用远程计算机,(3)保护用户因主机之间的文件存储系统导致的变化,(4)为了可靠和高效地传输,虽然用户...

      JAVA_API1.6文档(中文)

      java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 java.util.logging 提供 JavaTM 2 平台核心日志工具的类和接口。 java.util.prefs 此包允许应用程序...

      java api最新7.0

      java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 java.util.logging 提供 JavaTM 2 平台核心日志工具的类和接口。 java.util.prefs 此包允许应用程序...

      java开源包4

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      java开源包11

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      java开源包6

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      java开源包101

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      java开源包9

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      java开源包5

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      java开源包8

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      java开源包10

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      Java 1.6 API 中文 New

      java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 java.util.logging 提供 JavaTM 2 平台核心日志工具的类和接口。 java.util.prefs 此包允许应用程序...

      java开源包3

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

      JavaAPI1.6中文chm文档 part1

      java.util.jar 提供读写 JAR (Java ARchive) 文件格式的类,该格式基于具有可选清单文件的标准 ZIP 文件格式。 java.util.logging 提供 JavaTM 2 平台核心日志工具的类和接口。 java.util.prefs 此包允许应用程序...

      最新Java面试题视频网盘,Java面试题84集、java面试专属及面试必问课程

      │ Java面试题31.jdbc调用存储过程.mp4 │ Java面试题32.简单说一下你对jdbc的理解.mp4 │ Java面试题33.写一个jdbc的访问oracle的列子.mp4 │ Java面试题34.jdbc中preparedStatement比Statement的好处.mp4 │ Java...

      java开源包2

      php调用java类 Java批量作业执行框架 MyBatchFramework MyBatchFramework 是一个开源的轻量级的用以创建可靠的易管理的批量作业的Java包,主要特点是多线程、调度、JMX管理和批量执行报表,执行历史等。 SIP协议包...

    Global site tag (gtag.js) - Google Analytics