以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