本文原始碼:GitHub·點這里 || GitEE·點這里
一、Fork/Join框架
Java提供Fork/Join框架用于并行執行任務,核心的思想就是將一個大任務切分成多個小任務,然后匯總每個小任務的執行結果得到這個大任務的最終結果,
這種機制策略在分布式資料庫中非常常見,資料分布在不同的資料庫的副本中,在執行查詢時,每個服務都要跑查詢任務,最后在一個服務上做資料合并,或者提供一個中間引擎層,用來匯總資料:

核心流程:切分任務,模塊任務異步執行,單任務結果合并;在編程里面,通用的代碼不多,但是通用的思想卻隨處可見,
二、核心API和方法
1、編碼案例
基于1+2..+100的計算案例演示Fork/Join框架基礎用法,
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
public class ForkJoin01 {
public static void main (String[] args) {
int[] numArr = new int[100];
for (int i = 0; i < 100; i++) {
numArr[i] = i + 1;
}
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask =
pool.submit(new SumTask(numArr, 0, numArr.length));
System.out.println("合并計算結果: " + forkJoinTask.invoke());
pool.shutdown();
}
}
/**
* 執行緒任務
*/
class SumTask extends RecursiveTask<Integer> {
/*
* 切分任務塊的閾值
* 如果THRESHOLD=100
* 輸出:main【求和:(0...100)=5050】 合并計算結果: 5050
*/
private static final int THRESHOLD = 100;
private int arr[];
private int start;
private int over;
public SumTask(int[] arr, int start, int over) {
this.arr = arr;
this.start = start;
this.over = over;
}
// 求和計算
private Integer sumCalculate () {
Integer sum = 0;
for (int i = start; i < over; i++) {
sum += arr[i];
}
String task = "【求和:(" + start + "..." + over + ")=" + sum +"】";
System.out.println(Thread.currentThread().getName() + task);
return sum ;
}
@Override
protected Integer compute() {
if ((over - start) <= THRESHOLD) {
return sumCalculate();
}else {
int middle = (start + over) / 2;
SumTask left = new SumTask(arr, start, middle);
SumTask right = new SumTask(arr, middle, over);
left.fork();
right.fork();
return left.join() + right.join();
}
}
}
2、核心API說明
ForkJoinPool:執行緒池最大的特點就是分叉(fork)合并(join)模式,將一個大任務拆分成多個小任務,并行執行,再結合作業竊取演算法提高整體的執行效率,充分利用CPU資源,
ForkJoinTask:運行在ForkJoinPool的一個任務抽象,可以理解為類執行緒但是比執行緒輕量的物體,在ForkJoinPool中運行的少量ForkJoinWorkerThread可以持有大量的ForkJoinTask和它的子任務,同時也是一個輕量的Future,使用時應避免較長阻塞或IO,
繼承子類:
- RecursiveAction:遞回無回傳值的ForkJoinTask子類;
- RecursiveTask:遞回有回傳值的ForkJoinTask子類;
核心方法:
- fork():在當前執行緒運行的執行緒池中創建一個子任務;
- join():模塊子任務完成的時候回傳任務結果;
- invoke():執行任務,也可以實時等待最終執行結果;
3、核心策略說明
任務拆分

ForkJoinPool基于分治演算法,將大任務不斷拆分下去,每個子任務再拆分一半,直到達到最閾值設定的任務粒度為止,并且把任務放到不同的佇列里面,然后從最底層的任務開始執行計算,并且往上一層合并結果,這樣用相對少的執行緒處理大量的任務,
作業竊取演算法

大任務被分割為獨立的子任務,并且子任務分別放到不同的佇列里,并為每個佇列創建一個執行緒來執行佇列里的任務,假設執行緒A優先把分配到自己佇列里的任務執行完畢,此時如果執行緒E對應的佇列里還有任務等待執行,空閑的執行緒A會竊取執行緒E佇列里任務執行,并且為了減少竊取任務時執行緒A和被竊取任務執行緒E之間的發生競爭,竊取任務的執行緒A會從佇列的尾部獲取任務執行,被竊取任務執行緒E會從佇列的頭部獲取任務執行,
作業竊取演算法的優點:執行緒間的競爭很少,充分利用執行緒進行并行計算,但是在任務佇列里只有一個任務時,也可能會存在競爭情況,
三、應用案例分析
在后端系統的業務開發中,可用做權限校驗,批量定時任務狀態重繪等各種功能場景:

如上圖,假設資料的主鍵id分段如下,資料場景可能是資料源的連接資訊,或者產品有效期類似業務,都可以基于執行緒池任務處理:
權限校驗
基于資料源的連接資訊,判斷資料源是否可用,例如:判斷連接是否可用,用戶是否有庫表的讀寫權限,在資料源多的情況下,基于執行緒池快速校驗,
狀態重繪
在定時任務中,經常見到狀態類的重繪操作,例如判斷產品是否在有效期范圍內,在有效期范圍之外,把資料置為失效狀態,都可以利用執行緒池快速處理,
四、源代碼地址
GitHub·地址
https://github.com/cicadasmile/java-base-parent
GitEE·地址
https://gitee.com/cicadasmile/java-base-parent
推薦閱讀:Java并發系列
| 序號 | 文章標題 |
|---|---|
| 01 | Java并發:執行緒的創建方式,狀態周期管理 |
| 02 | Java并發:執行緒核心機制,基礎概念擴展 |
| 03 | Java并發:多執行緒并發訪問,同步控制 |
| 04 | Java并發:執行緒間通信,等待/通知機制 |
| 05 | Java并發:悲觀鎖和樂觀鎖機制 |
| 06 | Java并發:Lock機制下API用法詳解 |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/73363.html
標籤:Java
上一篇:精講RestTemplate第9篇-如何通過HTTP Basic Auth認證
下一篇:java面向物件(匯總)
