Java开发笔记(九十四)文件通道的性能优势
前面介绍了字节缓存的一堆概念,可能有的朋友还来不及消化,虽然文件通道的用法比起传统I/O有所简化,可是平白多了个操控繁琐的字节缓存,分明比较传统I/O更加复杂了。尽管字节缓存享有缓存方面的性能优势,但传统I/O也有缓存输入输出流呀,大家都有缓存机制,凭什么说NIO的文件处理更高效?之所以目前还看不出文件通道的性能优势,是因为前面介绍的仅限于它的基本用法,尚未涉及到高级特性,接下来阐述文件通道的真正杀手锏:使用通道复制文件。
复制文件的常规做法很简单,从源文件中读出数据,再将数据写进目标文件。采取文件通道和字节缓存的话,按照传统思路实现的文件复制代码示例如下:
// 使用文件通道和字节缓存复制文件
private static void copyChannelBuffer() {
// 分别创建源文件的文件通道,以及目标文件的文件通道
try (FileChannel src = new FileInputStream(mSrcName).getChannel();
FileChannel dest = new FileOutputStream(mDestName).getChannel()) {
int size = (int) src.size(); // 获取源文件的大小
// 分配指定大小的字节缓存
ByteBuffer buffer = ByteBuffer.allocateDirect(size);
src.read(buffer); // 把源文件中的数据读到字节缓存
buffer.flip(); // 从缓冲区读取数据之前,必须先调用flip方法
dest.write(buffer); // 把字节缓存中的数据写入目标文件
} catch (Exception e) {
e.printStackTrace();
}
}
上述代码与缓存输入输出流的实现代码看起来半斤八两,似乎程序运行效率也差不了多少,然而事实上的性能差距可大了。虽然应用程序的代码像是能够直接读写文件,但是应用程序依附于操作系统,它发出的文件读写指令需要经由操作系统来完成。也就是说,应用程序从磁盘文件读取数据的流程实际上是这样的:磁盘文件→操作系统→应用数据;应用程序把内存数据写入磁盘文件的流程则是这样的:应用数据→操作系统→磁盘文件。注意操作系统和应用程序分配到的存储空间是不一样的,设备的内存在运行时被划分为系统内存与用户内存两大块,其中系统内存装载了系统程序及其使用的内存空间,剩下的用户内存才能依次分给每个应用作为应用程序自身的内存空间。譬如电脑开机之后,刚进入桌面尚未打开任何一个应用程序,电脑内存就已经被消耗了相当一大块,正是操作系统自行占据系统内存的缘故。于是操作系统收到读文件指令之后,先把磁盘文件的数据读到系统内存当中,然后才由应用程序把系统内存中的数据读到应用内存;写文件操作同理,应用程序先把内存数据写到系统内存,再由操作系统把系统内存中的数据写入磁盘文件。因此,传统IO复制文件的完整数据流程正如下图所示:

由图示可见,传统IO在复制文件的过程中一共花费了四个步骤,分别是:步骤①(磁盘文件→系统内存)、步骤②(系统内存→应用内存)、步骤③(应用内存→系统内存)、步骤③(系统内存→磁盘文件),这四个步骤跑下来,难怪传统IO的处理效率高不到哪里去。
使用文件通道就不一样了,通道本身是专门负责I/O操作的处理机,字节缓存又是通道内部的存储空间,故而利用通道复制文件的话,既无需动用操作系统的系统内存,也无需动用应用程序的应用内存。那么使用文件通道完成文件复制功能仅仅需要两个步骤,即先将磁盘上的原文件内容读到通道中的字节缓存,再将字节缓存中的数据写入磁盘上的新文件,更直观的数据流转过程如下图所示:

由上图可见,采用通道复制文件才花了有两个步骤:步骤①(磁盘文件→字节缓存)、步骤②(字节缓存→磁盘文件),显然通道复制的性能要优于传统IO了。
针对文件复制功能,由于已经明确要把源文件的全部内容完成写入新文件,因此不必显式通过字节缓存完成数据的读取与写入动作,可以直接调用通道对象的transferTo方法或者transferFrom方法完成文件复制。其中transferTo方法操作的是源文件通道,它把数据传给目标文件通道;transferFrom方法操作的是目标文件通道,它从源文件通道传入数据。详细的调用代码例子如下所示:
// 使用文件通道直接复制文件
private static void copyChannelDirect() {
// 分别创建源文件的文件通道,以及目标文件的文件通道
try (FileChannel src = new FileInputStream(mSrcName).getChannel();
FileChannel dest = new FileOutputStream(mDestName).getChannel();) {
// 下面的transferTo和transferFrom都可以完成文件复制功能,选择其中一个即可
src.transferTo(0, src.size(), dest); // 操作源文件通道,把数据传给目标文件通道
//dest.transferFrom(src, 0, src.size()); // 操作目标文件通道,从源文件通道传入数据
} catch (Exception e) {
e.printStackTrace();
}
}
更多Java技术文章参见《Java开发笔记(序)章节目录》
Java开发笔记(九十四)文件通道的性能优势的更多相关文章
- Java开发笔记(四十二)日历工具的常见应用
前面介绍了日历工具Calendar的基本用法,乍看起来Calendar与Date两个半斤八两,似乎没有多大区别,那又何苦庸人自扰鼓捣一个新玩意呢?显然这样小瞧了Calendar,其实它的作用大着呢,接 ...
- Java开发笔记(四十五)成员属性与成员方法
前面介绍了许多数据类型,除了基本类型如整型int.双精度型double.布尔型boolean之外,还有高级一些的如包装整型Integer.字符串类型String.本地日期类型LocalDate等等,那 ...
- Java开发笔记(四十)日期与字符串的互相转换
前面介绍了如何通过Date工具获取各个时间数值,但是用户更喜欢形如“2018-11-24 23:04:18”这种结构清晰.简洁明了的字符串,而非啰里八唆依次汇报每个时间单位及其数值的描述.既然日期时间 ...
- Java开发笔记(四十一)日历工具Calendar
前面的文章提到,Date是Java最早的日期工具,估计当时的设计师是个技术宅男,未经过充分调研就拍脑袋写下了Date的源码,造成该工具存在先天不足,比如getYear方法返回的不是纯正的公元纪年.ge ...
- Java开发笔记(四十三)更好用的本地日期时间
话说Java一连设计了两套时间工具,分别是日期类型Date,以及日历类型Calendar,按理说用在编码开发中绰绰有余了.然而随着Java的日益广泛使用,人们还是发现了它们的种种弊端.且不说先天不良的 ...
- Java开发笔记(四十四)本地日期时间与字符串的互相转换
之前介绍Calendar的时候,提到日历实例无法直接输出格式化后的时间字符串,必须先把Calendar类型转换成Date类型,再通过格式化工具SimpleDateFormat获得字符串.而日期时间的格 ...
- Java开发笔记(四十六)类的构造方法
前面介绍了如何定义一个简单的类,以及它的成员属性和成员方法,从示例代码可以看到,不管是OrangeSimple还是OrangeMember,都要先利用关键字new创建一个实例,然后才能通过实例名称访问 ...
- Java开发笔记(四十七)关键字this的用法
前面介绍了类的基本定义,包括成员属性.成员方法.构造方法几个组成要素,可谓是具备了类的完整封装形态.不过在进行下一阶段的学习之前,有必要梳理一下前述的类定义代码,看看是否存在哪些需要优化的地方.首先观 ...
- Java开发笔记(四十八)类的简单继承
前面介绍了类的基本用法,主要是如何封装一个类的各项要素,包括成员属性.成员方法.构造方法等,想必大家对类的简单运用早已驾轻就熟.所谓“物以类聚,人以群分”,之所以某些事物会聚在一起,乃是因为它们拥有类 ...
随机推荐
- (linux)初学者学linux块设备驱动
面对不断升级的linux内核.GNU开发工具.linux环境下的各种图形库,很多linux应用程序开发人员和linux设备驱动开发人员即兴奋,又烦躁.兴奋的是新的软件软件.工具给我提供了更强大的功 ...
- HDU4763 Theme Section —— KMP next数组
题目链接:https://vjudge.net/problem/HDU-4763 Theme Section Time Limit: 2000/1000 MS (Java/Others) Mem ...
- 预处理指令#pragram
#pragma介绍 #pragma是一个预处理指令,pragma的中文意思是『编译指示』.它不是Objective-C中独有的东西(貌似在C/C++中使用比较多),最开始的设计初衷是为了保证代码在不同 ...
- 一些优秀的iOS第三方库
文章目录 Kits ProgressHUD 加载与刷新 图像 引导页 Views Others Kits RegexKitRegexKit是一个正则表达式工具类. JSONKitJSONKit是一个比 ...
- 「ZJOI2008」「LuoguP2590」树的统计(树链剖分
题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w. 我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u ...
- input type=password 浏览器会自动填充密码的问题
解决办法是在form上或input上添加autoComplete="off"这个属性. form表单的属性如下所示: 但是这个解决方案在谷歌和火狐上均有bug,下面来一个一个解决. ...
- 一个C#文件传输模块,支持断点续传
一个C#文件传输模块,支持断点续传 最近做一个程序需要传送文件,在网上找了好久也没找到好用的方案,于是自己写了一个,与大家分享,希望大家帮忙改进,拍砖欢迎-文件采取分块发送,每块单独校验,能够保证文件 ...
- 6-12 SVM小结
介绍了SVM的概念以及如何利用SVM进行一个身高体重的训练和预测.如果类别比较简单的话,那么在二维空间上它有可能就是一条直线.如果类别比较复杂,那么投影到高维空间上它就是一个超平面.所以SVM的本质它 ...
- HDU 5901 Count primes (模板题)
题意:给求 1 - n 区间内的素数个数,n <= 1e11. 析:模板题. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024 ...
- poj1308【并查集】
= =.如果输入的两个数相等.就不是一颗树啊,不能自己指向自己. 水.(瞎开的数组). //#include <bits/stdc++.h> #include<iostream> ...