Fork-Join框架
分治法
分治算法的基本思想將一個規模為N的問題,分解成K個規模較小的子問題,這些子問題相互獨立且月原問題性質相同。求解出子問題的解,合并得到原問題的解。
Fork-Join框架
任務分隔:體現了分而治之的思維,將一個大任務,進行拆分(Fork)成分成若干個小任務(拆到不能拆分為止)
執行任務并合并結果:分割的子任務分別放在雙端隊列中,然后啟動多個線程分別從雙端隊列中獲取子任務并執行,子任務完成后的結果放到另外一個里,啟動一個線程從這個隊列里取數據計算后返回
image.png
Fork-Join框架使用標準范式
- ForkJoinTask:創建ForkJoinTask任務,該類提供了fork()、join()機制,通常繼承它的RecursiveTask、RecursiveAction抽象類,RecursiveTask用于返回值任務,RecursiveAction用于無返回值任務
- ForkJoinPool:創建ForkJoinPool對象,用于執行ForkJoinTask任務
- 拆分任務并提交到上一級Task
- ForkJoinPool池invoke任務
- join所有的之任務結果
工作密取
多線程操作雙端隊列,每個消費者都有各自的雙端隊列。如果一個消費者完成了自己雙端隊列中的全部工作,那么它可以從其它消費者雙端隊列末尾秘密地獲取工作。
任務分割后的子任務會添加到當前線程所維護的雙端隊列中,進入隊列頭部,當一個工作線程的隊列任務暫時沒有時,會隨機從其他工作線程的雙端隊列尾部獲取一個任務執行
Fork-Join框架同步返回計算數組求和
import java.util.concurrent.RecursiveTask;
import com.shawntime.enjoy.architect.concurrency.SleepUtils;
public class ArraySumTask extends RecursiveTask<Long> {
private int[] array;
private int startIndex;
private int endIndex;
private int minNum;
public ArraySumTask(int[] array, int startIndex, int endIndex, int minNum) {
this.array = array;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.minNum = minNum;
}
@Override
protected Long compute() {
if (endIndex - startIndex < minNum) {
// 已經是最小了
Long result = 0L;
for (int i = startIndex; i <= endIndex; ++i) {
SleepUtils.sleepByMilliSeconds(1);
result += array[i];
}
return result;
} else {
System.out.println("繼續拆分....");
// 繼續拆分
int middle = (endIndex + startIndex) / 2;
ArraySumTask leftTask = new ArraySumTask(array, startIndex, middle, minNum);
ArraySumTask rightTask = new ArraySumTask(array, middle + 1, endIndex, minNum);
// 提交任務
invokeAll(leftTask, rightTask);
return leftTask.join() + rightTask.join();
}
}
}
int[] array = ArrayUtils.makeArray(arrayLength);
ForkJoinPool forkJoinPool = new ForkJoinPool();
int minNum = arrayLength / 50;
ForkJoinTask<Long> task = new ArraySumTask(array, 0, array.length - 1, minNum);
forkJoinPool.invoke(task);
Long value = task.join();
Fork-Join框架異步遍歷查找文件
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.RecursiveAction;
/**
* 異步的方式不帶返回值
*/
public class FindFileTask extends RecursiveAction {
private File file;
public FindFileTask(File file) {
this.file = file;
}
@Override
protected void compute() {
if (file == null) {
return;
}
File[] files = file.listFiles();
List<FindFileTask> tasks = new ArrayList<>();
for (File subFile : files) {
if (subFile.isDirectory()) {
FindFileTask task = new FindFileTask(subFile);
tasks.add(task);
} else {
if (subFile.getName().endsWith("mp4")) {
System.out.println(subFile.getAbsolutePath());
}
}
}
if (tasks.isEmpty()) {
return;
}
for (FindFileTask task : invokeAll(tasks)) {
task.join();
}
}
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
File file = new File("D:/");
FindFileTask findFiles = new FindFileTask(file);
// 異步提交
forkJoinPool.execute(findFiles);
int result = 0;
for (int i = 0; i < 1000; ++i) {
result += i;
}
System.out.println("main result : " + result);
findFiles.join(); // 阻塞
System.out.println("end....");
}