測試目錄:C:\Documents and Settings\Administrator\桌面\monkeytalk
private long getTotalSizeOfFilesInDir(final File file) {
if (file.isFile()) return file.length();
final File[] children = file.listFiles();
long total = 0;
if (children != null)
for (final File child : children)
total += getTotalSizeOfFilesInDir(child);
return total;
}
class SubDirectoriesAndSize {
final long size;
final List subDirectories;
// 建構子...
}
private SubDirectoriesAndSize getTotalAndSubDirs(final File file) {
long total = 0;
List subDirectories = new ArrayList<>();
if (file.isDirectory()) {
for (File child : file.listFiles()) {
if (child.isFile()) total += child.length();
else subDirectories.add(child);
}
}
return new SubDirectoriesAndSize(total, subDirectories);
}
private long getTotalSizeOfFilesInDir(final File file) throws ... {
ExecutorService service = Executors.newFixedThreadPool(100);
try {
long total = 0;
List directories = new ArrayList<>();
directories.add(file);
while (!directories.isEmpty()) {
List> partialResults = new ArrayList<>();
for (File directory : directories) {
partialResults.add(service.submit(() -> getTotalAndSubDirs(directory)));
}
directories.clear();
for (Future f : partialResults) {
SubDirectoriesAndSize r = f.get(100, TimeUnit.SECONDS);
directories.addAll(r.subDirectories);
total += r.size;
}
}
return total;
} finally { service.shutdown(); }
}
策略: 層級並行,每層目錄並發處理,收集子目錄供下一輪。
private long getTotalSizeOfFilesInDir(ExecutorService service, File file) throws ... {
if (file.isFile()) return file.length();
long total = 0;
File[] children = file.listFiles();
if (children != null) {
List> futures = new ArrayList<>();
for (File child : children) {
futures.add(service.submit(() -> getTotalSizeOfFilesInDir(service, child)));
}
for (Future f : futures) total += f.get(100, TimeUnit.SECONDS);
}
return total;
}
缺點: 遞歸深度大時會建立大量 Future,記憶體壓力大。
final AtomicLong pendingFileVisits = new AtomicLong();
final AtomicLong totalSize = new AtomicLong();
final CountDownLatch latch = new CountDownLatch(1);
private void updateTotalSizeOfFilesInDir(final File file) {
long fileSize = 0;
if (file.isFile()) fileSize = file.length();
else {
for (File child : file.listFiles()) {
if (child.isFile()) fileSize += child.length();
else {
pendingFileVisits.incrementAndGet();
service.execute(() -> updateTotalSizeOfFilesInDir(child));
}
}
}
totalSize.addAndGet(fileSize);
if (pendingFileVisits.decrementAndGet() == 0) latch.countDown();
}
機制: pendingFileVisits 追蹤進行中任務數,歸零時觸發 latch.countDown()。
final BlockingQueue fileSizes = new ArrayBlockingQueue<>(500);
final AtomicLong pendingFileVisits = new AtomicLong();
private void exploreDir(final File file) {
long fileSize = 0;
if (file.isFile()) fileSize = file.length();
else {
for (File child : file.listFiles()) {
if (child.isFile()) fileSize += child.length();
else startExploreDir(child); // 遞歸提交子目錄
}
}
fileSizes.put(fileSize); // 生產大小
pendingFileVisits.decrementAndGet();
}
// 消費者迴圈
while (pendingFileVisits.get() > 0 || fileSizes.size() > 0) {
Long size = fileSizes.poll(10, TimeUnit.SECONDS);
totalSize += size;
}
優勢: 解耦遍歷與累加,背壓自然形成(隊列滿即阻塞)。
private static class FileSizeFinder extends RecursiveTask {
final File file;
public FileSizeFinder(File file) { this.file = file; }
@Override
protected Long compute() {
long size = 0;
if (file.isFile()) return file.length();
File[] children = file.listFiles();
if (children != null) {
List> tasks = new ArrayList<>();
for (File child : children) {
if (child.isFile()) size += child.length();
else tasks.add(new FileSizeFinder(child));
}
for (ForkJoinTask task : invokeAll(tasks)) size += task.join();
}
return size;
}
}
// 使用
ForkJoinPool pool = new ForkJoinPool();
long total = pool.invoke(new FileSizeFinder(new File(path)));
核心: RecursiveTask 分治,invokeAll 並行執行子任務,join 合併結果。工作竊取演算法自動負載均衡。
| 方案 | 程式複雜度 | 記憶體 | 適用場景 |
|---|---|---|---|
| 單執行緒遞歸 | 最低 | 最低 | 小目錄、教學、快速原型 |
| FixedThreadPool+Future | 中 | 中 | 淺層寬目錄樹 |
| 遞歸 Callable | 低 | 高 | 深層目錄慎用 |
| CountDownLatch | 中高 | 中 | 需精確控制完成時機 |
| BlockingQueue | 中 | 可控 | 流式處理、背壓需求 |
| ForkJoinPool | 中 | 高效 | 通用首選、JDK 7+ |
ForkJoinPool 或 Files.walkFileTree (NIO.2)Files.walk 串流 + 並行 parallel()File.listFiles() 可能回傳 null(權限不足)