背景
? 當有時候一個檔案夾下有幾萬個幾十萬個檔案時,我們的桌面終端打開這個檔案夾可能會卡,或者將檔案進行批量上傳時,如果是在檔案夾下全選,那么基本上瀏覽器就卡死了,當然也不能這樣子操作滴~
? 題主最近就遇到這樣一個問題,批量上傳檔案,有幾萬個,擔心全選會搞崩瀏覽器或者cmd終端,于是打算將資料分組,分批次上傳,減少風險壓力,可能有的同學會說,那簡單嘛,直接ctrl C+V完事兒,但是人這個眼睛吶,越集中注意力看一個字,就越不覺得它像個字,所以難免會出錯的,而且拖動也會很卡,
作為一個搞電腦的工程師(程式猿),能用電腦解決的,怎么能浪費體力呢[滑稽]
參考步驟
? 其實要實作這么一個東西,很簡單的,二話不多說,直接一個for回圈搞定 歐耶!但是呀,那個速度呀,難以忍受,如果分組這個檔案還需要去做一些額外的操作,那豈不是更慢,說到這,想到以前讀書學習大資料的時候,分詞計算map-reduce那每次一跑就是一個小時過去了,所以,光寫出來不得行,還得優化,而且分組的時候有一些注意點要注意,
? 題主的大致步驟如下:
- 打開檔案夾,遍歷檔案;
- 啟用執行緒池,多執行緒跑批任務,加快速度;
- 計數檔案夾中檔案個數,達到指定數量,建立下一個檔案夾;
- 使用新的檔案夾繼續移動或復制檔案,操作程序中進行重命名;重復3,4步驟
- 操作完畢,等待執行緒池任務處理完畢,銷毀,
代碼實作
? 說了那么多,還是直接上代碼吧:)
import java.io.File;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.*;
public class TestReduceFile {
/**
* 檔案計數
*/
private static volatile int currentFileNum = 0;
private static final String initFilePathName = "/Users/anhaoo/test/reduceFile/reduce";
private static volatile String currentFilePathName;
public static void main(String[] args) {
groupFile("/User/anhaoo/test/big_pdf");
}
/**
* 將檔案分組:分成一個檔案夾多少個檔案這種
* @param oldFilePath
*/
private static void groupFile(String oldFilePath) {
File file = new File(oldFilePath);
File[] fileList = file.listFiles();
// 執行緒池批處理:鏈表阻塞佇列
ExecutorService executor = Executors.newFixedThreadPool(80);
if (Objects.nonNull(fileList) && fileList.length > 0) {
// System.out.println(fileList.length);
Arrays.stream(fileList).forEach(item -> {
// 執行緒池提交任務處理
if (!item.isDirectory()) {
executor.execute(() -> groupFileSub(item));
}
});
}
// shutdown 等待任務全部執行完畢 銷毀
executor.shutdown();
}
/**
* 分組檔案,每滿n個檔案生成下一個檔案夾,然后將后續遍歷的檔案移動到下一個檔案夾中
* @param oldFile
*/
private static synchronized void groupFileSub(File oldFile) {
// 判斷是否需要新生成檔案夾,一個檔案夾下放700個
if (currentFileNum % 700 == 0) {
// reduce0,reduce1,reduce2......
String newFileFolder = initFilePathName.concat(String.valueOf(currentFileNum/700));
// 將新的檔案夾名賦值給共享變數
currentFilePathName = newFileFolder;
}
File aimFilePath = new File(currentFilePathName);
if (!aimFilePath.exists()) {
aimFilePath.mkdirs();
}
try {
// 移動檔案時順便重命名檔案
String oldFileName = oldFile.getName();
String[] fileNameArr = oldFileName.split("_");
// 加下面這個if的原因 是因為mac電腦下有一個隱藏的.DS_Store檔案,它按我的規則重命名時會影響我的操作
// 所以判斷一下,不然我這里會拋越界例外:)
if (fileNameArr.length < 3) {
return;
}
String uuid = fileNameArr[2];
String newFileName = "/pdfFile_".concat(uuid);
File aimFile = new File(aimFilePath + File.separator + newFileName);
currentFileNum++;
oldFile.renameTo(aimFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
? 代碼就如上了,效果如圖所示:


701 是因為有一個隱藏檔案了哈!
注意事項
? 有幾個點需要注意一下:上面程式中使用了volatile和synchronized,以及執行緒池等工具,
- 使用執行緒池,是為了多個執行緒一起處理,加快效率;
- 使用volatile修飾了兩個變數,因為currentFileNum會實時變化,currentFilePathName檔案夾也會在執行程序中發生變化
? 可能有的同學會有疑惑,既然使用了volatile關鍵保證了多執行緒變數的可見性,那為什么還要使用synchronize持鎖同步呢?哈哈哈,剛開始我也是這么認為的,沒有使用鎖,直接跑,但是每次跑完后每個檔案夾的數量不僅不相等而且數量還不一致,不止700個多,后來仔細一想,volatile雖然保持了變數的可見性,但是當多個執行緒拿到這個變數是將變數副本拷貝到自己的堆疊記憶體中,只能保證在獲取變數的時候是最新的,但是CPU指令的執行是哪個執行緒搶到了就去執行,所以可能剛好那個時候其他執行緒又將變數修改了,因為計數變數currentFileNum在不停地自增,導致執行緒不安全,不符合我們的預期效果,這個也再一次證明了一個結論:
volatile只是保證了可見性,但是執行緒變數的安全它無法保證;
所以在這個方法上加了一個鎖,保證執行緒的安全性;
? 上述就是本文想分享的東西了,如果有更好的方法或者文章中有不足之處歡迎大家指出,共同進步才是我們的宗旨!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/266616.html
標籤:其他
上一篇:Python中的那些“坑”
