以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是咱们要存储读取到用户空间的缓冲区。参看read(byte b[])方法的源码,可知,它会在内部再调用readBytes(b, 0, b.length)方法,并且readBytes(b, 0, b.length)方法是一个native方法(即本地方法),最终经过这个本地方法来发起一次系统调用,即调用系统内核的read()方法,内核从磁盘读取数据到内核缓冲区,这个过程由磁盘控制器经过DMA操做将数据从磁盘读取内核缓冲区,此过程不依赖于CPU。而后用户进程再将数据从内核缓冲区拷贝到用户空间缓冲区。用户进程再从用户空间缓冲区中读取数据。由于用户进程是不能够直接访问硬件的。因此须要经过内核来充当中间人的做用来实现文件的读取。整个过程以下图所示:java
下面就看下使用IO,BufferedIO和NIO分别实现的文件复制耗时比较:11兆音频文件apache
传统IO方法实现文件拷贝耗时:21ms
利用NIO文件通道方法实现文件拷贝耗时:16ms
利用NIO文件内存映射及文件通道实现文件拷贝耗时:7ms
利用FileUtils文件拷贝工具类耗时:53msapp
package com.maystar.utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;工具
import org.apache.commons.io.FileUtils;性能
public class FileCopyTest {
public static void main(String[] args) throws Exception {
String sourcePath = "F:\\glzmv.mp3";
String destPath1 = "F:\\glzmvCopy1.mp3";
String destPath2 = "F:\\glzmvCopy2.mp3";
String destPath3 = "F:\\glzmvCopy3.mp3";
String destPath4 = "F:\\glzmvCopy4.mp3";
long t1 = System.currentTimeMillis();
traditionalCopy(sourcePath,destPath1);
long t2 = System.currentTimeMillis();
System.out.println("传统IO方法实现文件拷贝耗时:" + (t2-t1) + "ms");
nioCopy(sourcePath,destPath2);
long t3 = System.currentTimeMillis();
System.out.println("利用NIO文件通道方法实现文件拷贝耗时:" + (t3-t2) + "ms");
nioCopy2(sourcePath,destPath3);
long t4 = System.currentTimeMillis();
System.out.println("利用NIO文件内存映射及文件通道实现文件拷贝耗时:" + (t4-t3) + "ms");
nioCopy3(sourcePath,destPath4);
long t5 = System.currentTimeMillis();
System.out.println("利用FileUtils文件拷贝耗时:" + (t5-t4) + "ms");
}
private static void nioCopy3(String sourcePath, String destPath) throws Exception {
File source = new File(sourcePath);
File dest = new File(destPath);
FileUtils.copyFile(source, dest);//查看源码commons-io-2.4也使用的是nio操做,实现相似nioCopy操做,可是为何效率比nioCopy要低,缘由是在FileUtils.copyFile执行doCopyFile完成调用IOUtils工具类关闭流操做,根据不一样类型的流调用对应的构造方法。
}
private static void nioCopy2(String sourcePath, String destPath) throws Exception {
File source = new File(sourcePath);
File dest = new File(destPath);
if(!dest.exists()) {
dest.createNewFile();
}
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest);
FileChannel sourceCh = fis.getChannel();
FileChannel destCh = fos.getChannel();
MappedByteBuffer mbb = sourceCh.map(FileChannel.MapMode.READ_ONLY, 0, sourceCh.size());
destCh.write(mbb);
sourceCh.close();
destCh.close();
}
private static void traditionalCopy(String sourcePath, String destPath) throws Exception{
File source = new File(sourcePath);
File dest = new File(destPath);
if(!dest.exists()) {
dest.createNewFile();
}
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest);
byte [] buf = new byte [fis.available()];
int len = 0;
while((len = fis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fis.close();
fos.close();
}
private static void nioCopy(String sourcePath, String destPath) throws Exception{
File source = new File(sourcePath);
File dest = new File(destPath);
if(!dest.exists()) {
dest.createNewFile();
}
FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(dest);
FileChannel sourceCh = fis.getChannel();
FileChannel destCh = fos.getChannel();
destCh.transferFrom(sourceCh, 0, sourceCh.size());
sourceCh.close();
destCh.close();
}
} spa