Java利用多線程復(fù)制文件
前言
復(fù)制一個(gè)文件,是學(xué)習(xí)IO流時(shí)最基本的操作。你可以使用字節(jié)型文件流,也可以使用高級(jí)緩沖流。
但是,它們都是單線程的。
如果需要復(fù)制一個(gè)大型文件,單線程的復(fù)制一般而言是不能夠充分發(fā)揮CPU以及內(nèi)存的性能。這時(shí)候就需要利用多線程來(lái)復(fù)制文件。
多線程的讀:
我們很自然地想到,利用FileInputStream類(lèi)的skip()方法,可以跳著讀,這就對(duì)多線程比較友好,啟動(dòng)多個(gè)線程,第一個(gè)線程讀一部分,第二個(gè)線程跳過(guò)一部分字節(jié)再讀,這沒(méi)有問(wèn)題。
但是如果要寫(xiě)呢?我們知道FileOutputStream類(lèi)是沒(méi)有與skip類(lèi)似的方法的,也就是說(shuō),它不能跳著寫(xiě),這就很麻煩。
這就意味著,如果需要利用多線程復(fù)制一個(gè)文件,那么首先得把這個(gè)文件利用多線程并發(fā),讀取并同時(shí)寫(xiě)入成多個(gè)文件碎片,然后再利用單線程去一一讀取這些文件碎片,把它們的內(nèi)容寫(xiě)入到一個(gè)完整的文件中。必要的話,最后再刪除這些中間文件碎片。
這個(gè)過(guò)程,復(fù)雜,且性能不高。涉及到兩套讀寫(xiě),前面多線程讀寫(xiě),后面單線程讀寫(xiě)。很顯然這個(gè)過(guò)程雖然使用到了多線程但它不能提高性能,反而降低了性能。
既然雞肋點(diǎn)在于FileOutputStream不能跳著寫(xiě),那么就找一個(gè)能跳著寫(xiě)的類(lèi)吧。
RandomAccessFile
RandomAccessFile是java Io體系中功能最豐富的文件內(nèi)容訪問(wèn)類(lèi)。即可以讀取文件內(nèi)容,也可以向文件中寫(xiě)入內(nèi)容。但是和其他輸入/輸入流不同的是,程序可以直接跳到文件的任意位置來(lái)讀寫(xiě)數(shù)據(jù)。
RandomAccessFile包含了以下兩個(gè)方法來(lái)操作文件的記錄指針:
- long getFilePointer(); 返回文件記錄指針的當(dāng)前位置
- void seek(long pos); 將文件記錄指針定位到pos位置
有了RandomAccessFile這個(gè)類(lèi),上面的問(wèn)題就迎刃而解,因?yàn)樗畲蟮暮锰幘褪强梢詫?shí)現(xiàn)從指定位置寫(xiě)入一些數(shù)據(jù)到文件中!
順便說(shuō)一下它的構(gòu)造方法:
RandomAccessFile(File file, String mode)
RandomAccessFile(String name, String mode)
mode表示RandomAccessFile的訪問(wèn)模式,她有4個(gè)值:
- “r”:只有讀的權(quán)限,如果你試圖去執(zhí)行寫(xiě)入方法,則拋出IOException異常。
- “rw”:有讀和寫(xiě) 兩個(gè)權(quán)限,如果該文件不存在,則會(huì)創(chuàng)建該文件。
- “rws”:相對(duì)于”rw” 模式,還要求對(duì)文件內(nèi)容或元數(shù)據(jù)的每個(gè)更新都同步寫(xiě)入到底層設(shè)備。
- “rwd”:相對(duì)于”rw” 模式,還要求對(duì)文件內(nèi)容每個(gè)更新都同步寫(xiě)入到底層設(shè)備。
一般而言我們使用“r”和“rw”就夠了,后面那兩個(gè)我也不懂干嘛的。
代碼
package testThread.file_threading; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.RandomAccessFile; public class ThreadFileCopy extends Thread { ? ? private String srcFileStr;//源文件的路徑 ? ? private String desFileStr;//目標(biāo)文件的路徑 ,des --> destination目的地 ? ? private long skipLen;//跳過(guò)多少個(gè)字節(jié)開(kāi)始讀/寫(xiě) ? ? private long workload;//總共要讀/寫(xiě)多少個(gè)字節(jié) ? ? private final int IO_UNIT = 1024;//每次讀寫(xiě)的基本單位(1024個(gè)字節(jié)) ? ? public ThreadFileCopy(String srcFileStr, String desFileStr, long skipLen, long workload) { ? ? ? ? this.srcFileStr = srcFileStr; ? ? ? ? this.desFileStr = desFileStr; ? ? ? ? this.skipLen = skipLen; ? ? ? ? this.workload = workload; ? ? } ? ? public void run(){ ? ? ? ? FileInputStream fis = null; ? ? ? ? BufferedInputStream bis = null;//利用高級(jí)緩沖流,加快讀的速度 ? ? ? ? RandomAccessFile raf = null; ? ? ? ? try { ? ? ? ? ? ? fis = new FileInputStream(srcFileStr); ? ? ? ? ? ? bis = new BufferedInputStream(fis); ? ? ? ? ? ? raf = new RandomAccessFile(desFileStr,"rw"); ? ? ? ? ? ? bis.skip(this.skipLen);//跳過(guò)一部分字節(jié)開(kāi)始讀 ? ? ? ? ? ? raf.seek(this.skipLen);//跳過(guò)一部分字節(jié)開(kāi)始寫(xiě) ? ? ? ? ? ? byte[] bytes = new byte[IO_UNIT]; ? ? ? ? ? ? //根據(jù)總共需要復(fù)制的字節(jié)數(shù) 和 讀寫(xiě)的基本單元 計(jì)算出一共需要讀寫(xiě)的次數(shù),利用讀寫(xiě)次數(shù)控制循環(huán) ? ? ? ? ? ? long io_num = this.workload/IO_UNIT + 1;//因?yàn)閣orkload/1024 很可能不能整除,會(huì)有余數(shù) ? ? ? ? ? ? if(this.workload % IO_UNIT == 0) ? ? ? ? ? ? ? ? io_num--;//如果碰巧整除,讀寫(xiě)次數(shù)減一 ? ? ? ? ? ? //count表示讀取的有效字節(jié)數(shù),雖然count不參與控制循環(huán)結(jié)束, ? ? ? ? ? ? // 但是它能有效避免最后一次讀取出的byte數(shù)組中有大量空字節(jié)寫(xiě)入到文件中,導(dǎo)致復(fù)制出的文件稍稍變大 ? ? ? ? ? ? int count = bis.read(bytes); ? ? ? ? ? ? while (io_num != 0){ ? ? ? ? ? ? ? ? raf.write(bytes,0,count); ? ? ? ? ? ? ? ? count = bis.read(bytes,0,count);//重新計(jì)算count的值 ? ? ? ? ? ? ? ? io_num--; ? ? ? ? ? ? } ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? }finally { ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? if (fis != null) ? ? ? ? ? ? ? ? ? ? fis.close(); ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? if (bis != null) ? ? ? ? ? ? ? ? ? ? bis.close(); ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? if (raf != null) ? ? ? ? ? ? ? ? ? ? raf.close(); ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? } ? ? } }
測(cè)試
package testThread.file_threading; import java.io.File; public class TestMain { ? ? public static void main(String[] args) { ? ? ? ? int thread_num = 5;//創(chuàng)建5個(gè)線程讀寫(xiě) ? ? ? ? String srcFileStr = "E:\\test\\123.flv"; ? ? ? ? String desFileStr = "E:\\test\\thread.flv"; ? ? ? ? File srcFile = new File(srcFileStr); ? ? ? ? long workload = srcFile.length()/thread_num;//總共要讀/寫(xiě)多少個(gè)字節(jié) ? ? ? ? //用一個(gè)數(shù)組來(lái)存儲(chǔ)每個(gè)線程跳過(guò)的字節(jié)數(shù) ? ? ? ? long[] skipLenArr = new long[thread_num]; ? ? ? ? for(int i = 0;i<skipLenArr.length;i++){ ? ? ? ? ? ? skipLenArr[i] = i*workload; ? ? ? ? } ? ? ? ? //用一個(gè)數(shù)組來(lái)存儲(chǔ)所有的線程 ? ? ? ? ThreadFileCopy[] tfcs = new ThreadFileCopy[thread_num]; ? ? ? ? //初始化所有線程 ? ? ? ? for(int i = 0;i<tfcs.length;i++){ ? ? ? ? ? ? tfcs[i] = new ThreadFileCopy(srcFileStr,desFileStr,skipLenArr[i],workload); ? ? ? ? } ? ? ? ? //讓所有線程進(jìn)入就緒狀態(tài) ? ? ? ? for(int i = 0;i<tfcs.length;i++){ ? ? ? ? ? ? tfcs[i].start(); ? ? ? ? } ? ? ? ? System.out.println("復(fù)制完畢!"); ? ? } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java中過(guò)濾器、監(jiān)聽(tīng)器和攔截器的區(qū)別詳解
這篇文章主要介紹了Java中過(guò)濾器、監(jiān)聽(tīng)器和攔截器的區(qū)別詳解,有些朋友可能不了解過(guò)濾器、監(jiān)聽(tīng)器和攔截器的區(qū)別,本文就來(lái)詳細(xì)講一下,相信看完你會(huì)有所收獲,需要的朋友可以參考下2024-01-01java 中接口和抽象類(lèi)的區(qū)別與對(duì)比
這篇文章主要介紹了java 中接口和抽象類(lèi)的區(qū)別與對(duì)比的相關(guān)資料,這里詳細(xì)說(shuō)明他們之家的區(qū)別,需要的朋友可以參考下2017-08-08Spring事件監(jiān)聽(tīng)器ApplicationListener源碼詳解
這篇文章主要介紹了Spring事件監(jiān)聽(tīng)器ApplicationListener源碼詳解,ApplicationEvent以及Listener是Spring為我們提供的一個(gè)事件監(jiān)聽(tīng)、訂閱的實(shí)現(xiàn),內(nèi)部實(shí)現(xiàn)原理是觀察者設(shè)計(jì)模式,需要的朋友可以參考下2023-05-05Java中ByteArrayOutputStream亂碼問(wèn)題解決
本文主要介紹了Java中ByteArrayOutputStream亂碼問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07基于SpringBoot實(shí)現(xiàn)自動(dòng)裝配返回屬性的設(shè)計(jì)思路
這篇文章主要介紹了基于SpringBoot實(shí)現(xiàn)自動(dòng)裝配返回屬性,這里涉及到的技術(shù)知識(shí)點(diǎn)有注解解析器,為什么用ResponseBodyAdvice這里解析?不在Filter,Interceptors,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2022-03-03Spring中@RequestMapping、@PostMapping、@GetMapping的實(shí)現(xiàn)
RequestMapping、@PostMapping和@GetMapping是三個(gè)非常常用的注解,本文就來(lái)介紹一下這三種注解的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下2024-07-07