本文展示BIO,NIO複製文件的不同寫法,和一丟丟的效率對比。
在演示NIO之前 ,先簡單說一下 NIO
在BIO中,根據方向的不同,分爲輸入流,輸出流,而在NIO中,就沒有這個概念了,NIO中,提出了Channel的概念,一個通道是可以讀也可以寫的。channel通過和Buffer的交互,可以往buffer寫數據也可以讀數據,這是怎麼實現的呢?
首先,Buffer是一個數組,有三個重要的指針。
buffer的大小/容量 - Capacity
當前讀/寫的位置 - Position
信息末尾的位置 - limit
初始化的時候,他們的分佈是這個樣子的(找文件夾的圖片真的太難翻了,我直接截圖我的MD)。
假設寫了3份數據。
這個時候想讀取數據了,這個時候調用flip()函數,會發生這樣的過程。
position回到起點,limit指針在原來position的位置。
假設數據讀取完畢,調用clear()函數,buffer又會變成原來的樣子,變成寫入模式。
如果在讀數據的時候,並沒有讀取完數據,還想下次繼續讀取且不影響寫入呢
此時調用compact()函數,buffer會拷貝沒有讀取完的數據從起點開始存放,並且調整指針。
這樣大概明白了吧,但是Channel之間也是可以之間通信的。
下面展示覆制文件的用法
爲了使用上的方便,首先定義一個文件複製的接口。
public interface FileCopyRunner {
void copyFile(File source,File target);
}
main函數中分別實現不同的方式:
FileCopyRunner noBufferStreamCopy;
FileCopyRunner bufferStreamCopy;
FileCopyRunner nioBufferCopy;
//two channel
FileCopyRunner nioTransferCopy;
noBufferStreamCopy:
noBufferStreamCopy = new FileCopyRunner() {
@Override
public void copyFile(File source, File target) {
InputStream fin = null;
OutputStream fout = null;
try {
fin = new FileInputStream(source);
fout = new FileOutputStream(target);
int result;
while ((result = fin.read()) != -1) {
fout.write(result);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
@Override
public String toString() {
return "noBufferStreamCopy";
}
};
bufferStreamCopy:
bufferStreamCopy = new FileCopyRunner() {
@Override
public void copyFile(File source, File target) {
InputStream fin = null;
OutputStream fout = null;
try {
fin = new BufferedInputStream(new FileInputStream(source));
fout = new BufferedOutputStream(new FileOutputStream(target));
byte[] buffer = new byte[1024];
int result;
while ((result = fin.read(buffer)) != -1){
fout.write(buffer,0,result);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
@Override
public String toString() {
return "bufferStreamCopy";
}
};
nioBufferCopy:
nioBufferCopy = new FileCopyRunner() {
@Override
public String toString() {
return "nioBufferCopy";
}
@Override
public void copyFile(File source, File target) {
FileChannel fin = null;
FileChannel fout = null;
try {
fin = new FileInputStream(source).getChannel();
fout = new FileOutputStream(target).getChannel();
ByteBuffer buffer =ByteBuffer.allocate(1024);
while ((fin.read(buffer)) != -1){
//轉換模式
buffer.flip();
while (buffer.hasRemaining()){
fout.write(buffer);
}
//寫完後,再把指針還原
buffer .clear();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
};
nioTransferCopy:
nioTransferCopy = new FileCopyRunner() {
@Override
public String toString() {
return "nioBufferCopy";
}
@Override
public void copyFile(File source, File target) {
FileChannel fin = null;
FileChannel fout = null;
try {
fin = new FileInputStream(source).getChannel();
fout = new FileOutputStream(target).getChannel();
long transfer =0L;
long size = fin.size();
//開始拷貝數據的位置,
while (transfer != fin.size()) {
transfer += fin.transferTo(0, size, fout);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
close(fin);
close(fout);
}
}
};
平均複製粘貼5次比較效率 :
private static final int ROUNDS = 5;
private static void benchmark(FileCopyRunner test,File source,File target){
long elapsed = 0L;
for (int i = 0; i <ROUNDS ; i++) {
long startTime = System.currentTimeMillis();
test.copyFile(source,target);
long endTime = System.currentTimeMillis();
elapsed+=endTime-startTime;
//最後要刪的哈
target.delete();
}
System.out.println(test + ":" + elapsed/ROUNDS );
}
File sourceFile = new File("C:/Users/12479/Desktop/SB核心篇章/源碼、資料、課件.rar");
File targetFile = new File("C:/Users/12479/Desktop/SB核心篇章/n源碼、資料、課件.rar");
// benchmark(noBufferStreamCopy,sourceFile,targetFile);
benchmark(bufferStreamCopy,sourceFile,targetFile);
benchmark(nioBufferCopy,sourceFile,targetFile);
benchmark(nioTransferCopy,sourceFile,targetFile);
知道我爲啥把第一個註釋了嗎?最開始我是沒有註釋的,但是驚人的事情發生了,我開始測試的文件是1.5Gb,5個來回7個多G吧?我的天吶,我12點吃飯我吃完飯等到1點啊!!
我就直接貼結果了:
/**
- 80M
- noBufferStreamCopy:326448
- bufferStreamCopy:159
- nioBufferCopy:486
- nioBufferCopy:81
- 1.5GB
- bufferStreamCopy:27624
- nioBufferCopy:35141
- nioBufferCopy:21027
*/
下篇預告:基於NIO模型實現聊天室