Fork/Join 核心思想
- 分割:大任務遞歸拆分為足夠小的子任務。
- 執行:子任務放入工作線程雙端隊列(Deque),線程從隊首取任務。
- 合併:子任務結果放入結果隊列,專門線程聚合最終結果。
兩大核心類
- ForkJoinTask:抽象任務類,提供
fork()(異步執行)、join()(阻塞獲取結果)。兩個常用子類:
RecursiveAction:無返回值任務。
RecursiveTask:有返回值任務(泛型 V)。
- ForkJoinPool:執行器,管理工作線程與工作窩盜演算法:空閒線程從其他線程隊列尾部竊取任務,實現負載均衡。
實戰:統計目錄下文件總數(含子目錄)
package com.thread.test.thread;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
public class MyForkJoin {
public static void main(String[] args) {
MyTask task = new MyTask(new File("D:\\MPS"));
Integer totalFiles = new ForkJoinPool().invoke(task);
System.out.println("Total files: " + totalFiles);
}
}
class MyTask extends RecursiveTask {
private final File file;
MyTask(File file) { this.file = file; }
@Override
protected Integer compute() {
int count = 0;
List subTasks = new ArrayList<>();
if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
if (child.isDirectory()) {
subTasks.add(new MyTask(child)); // 繼續分割
} else {
count++; // 文件直接計數
}
}
}
} else {
return 1; // 單文件
}
// 批量 fork + join,等同於循環 fork 再循環 join
for (MyTask t : invokeAll(subTasks)) {
count += t.join();
}
return count;
}
}
關鍵點解析
invokeAll(subTasks):內部自動 fork() 所有子任務並返回完成列表,比手動雙循環更簡潔高效。
- 門檻值隱性控制:
file.isDirectory() 為分割條件,單文件直接返回,自然形成基準情況。
- 適用場景:CPU 密集型、可分治遞歸問題(排序、搜索、文件遍歷、圖像處理)。
項目地址
https://github.com/windwant/threadtest