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开发笔记(四十八)类的简单继承
前面介绍了类的基本用法,主要是如何封装一个类的各项要素,包括成员属性.成员方法.构造方法等,想必大家对类的简单运用早已驾轻就熟.所谓“物以类聚,人以群分”,之所以某些事物会聚在一起,乃是因为它们拥有类 ...
随机推荐
- iOS 获取WIFI SSID及MAC地址
NSString *ssid = @"Not Found"; NSString *macIp = @"Not Found"; CFArrayRef myArra ...
- tload
tload命令以图形化的方式输出当前系统的平均负载到指定的终端.假设不给予终端机编号,则会在执行tload指令的终端机显示负载情形. 语法 tload(选项)(参数) 选项 -s:指定闲时的刻度: - ...
- Linux系统中的运行级别
什么是运行级呢?简单的说,运行级就是操作系统当前正在运行的功能级别. 它让一些程序在一个级别启动,而另外一个级别的时候不启动. Linux系统的有效登录模式有0~9共十种,不过沿用UNIX系统的至多6 ...
- 如何生成Android的keystore文件
步骤 1 找到本机电脑上jdk安装的目录,使用cmd命令打开命令窗口,输入cd jdk目录(替换成你的jdk的bin目录),进入到jdk的bin目录,接下来你才可以使用jdk的命令进行操作 步骤 ...
- Bootstrap-CSS:辅助类
ylbtech-Bootstrap-CSS:辅助类 1.返回顶部 1. Bootstrap 辅助类 本章将讨论 Bootstrap 中的一些可能会派上用场的辅助类. 文本 以下不同的类展示了不同的文本 ...
- wxPython学习笔记1
wxpython介绍: wxPython 是 Python 语言的一套优秀的 GUI 图形库,允许 Python 程序员很方便的创建完整的.功能键全的 GUI 用户界面. wxPython 是作为优 ...
- zabbix忘记登录密码重置方法
1.忘记zabbix之后,先用mysql查看一下: [root@centos7-106 ~]# mysql -uroot -p -e "select * from zabbix.users\ ...
- Goroutine被动调度之一(18)
本文是<Go语言调度器源代码情景分析>系列的第18篇,也是第四章<Goroutine被动调度>的第1小节. 前一章我们详细分析了调度器的调度策略,即调度器如何选取下一个进入运行 ...
- BestCoder Round #74 (div.1) 1002Shortest Path(hdoj5636)
哈哈哈哈,我就知道这道题目再扔给我,我还是不会,就是这么菜,哈哈哈 一开始官方题解就没搞懂-然后就看了一下别人的代码,水水过就算了.今天拿到-GG: 题意: 一开始,有一张原图,有一条长度为n的链. ...
- 基于FBX SDK的FBX模型解析与加载 -(四)
8. 骨骼蒙皮动画 骨骼蒙皮动画是当前游戏引擎中最常用的一种动画方式,关于其基本原理网络上的资料较多,关于到涉及的其它较复杂操作,如插值.融合等在这里也就先不再讨论了,而且其实现方式也与具体引擎的动作 ...