介紹:
Fork/Join並行是獲得良好的並行效能的最簡單高效的設計技術。他是分治(Divide-and-Conquer)演算法的的並行實現,它的典型應用形式:
Result solve(Problem problem) { if (problem is small)directly solve problem else {split problem into independent partsfork new subtasks to solve each partjoin all subtaskscompose result from subresults}}
fork操作啟動一個新的並行fork/join子任務,join操作則導致當前線程等待直到子任務執行完成。fork/join演算法,就像其它分治演算法一樣,幾乎經常是不斷的遞迴,重複劃分子任務——直到它們的規模小到能夠使用簡單、簡短的串列方法完成。
思想:
1、分而治之,將大任務分解為小任務,再合并最終結果
2、工作竊取調度策略
(1)每個worker線程利用它自己的調度隊列維護可執行任務。
(2)隊列是雙端的,支援LIFO(last-in-first-out)也支援FIFO(first-in-first-out)。
(3)一個任務fork的子任務,只會push到它所線上程的隊列。
(4)背景工作執行緒使用LIFO通過pop處理它自己隊列中的線程。
(5)當線程自己沒有待處理任務時,它嘗試去隨機讀取(竊取)一個worker線程的工作隊列任務(使用FIFO)。
(6)當線程進入join操作,它開始處理其它線程的任務(自己的已經處理完了),直到目標任務完成(通過isDone方法)。因此,所有任務都無阻塞的完成了。
(7)當一個背景工作執行緒沒有任務了,並且嘗試從其它線程處竊取也失敗了,它讓出資源(通過使用yields, sleeps或者其它優先順序調整)並且隨後會再次啟用,直到所有背景工作執行緒都空閑了——此時,它們都阻塞在等待另一個頂層線程的調用。
兩種介面:
RecursiveTask: 帶傳回值,底層用的是Callable介面
RecursiveAction:無傳回值,Runnable介面
ForkJoinPool源碼中重要欄位:
eventCount三種含義: 1、線程被掛起次數 2、小於零表示沒有被啟用狀態 3、標明為第幾個背景工作執行緒nextWait: 由於fork/join維護著一個掛起線程鏈,這個鏈是棧結構,本欄位就是遍曆掛起棧用的qlock: 起到鎖的作用top、base : 分別是儲存背景工作執行緒隊列的頭和尾poolIndex: 指的是背景工作執行緒在隊列中的下標
核心方法scan流程解析:
設計亮點:
1、互幫互助的設計思路
2、多欄位打包操作
範例程式碼: 完成累加運算
import java.util.ArrayList;import java.util.concurrent.ForkJoinPool;import java.util.concurrent.ForkJoinTask;import java.util.concurrent.RecursiveTask;/** * @Author: Allen * @Description: 用ForkJoinPool實現累加操作 * @Date: Created in 12:42 2018/2/27 * @Modify By: */public class CountTask extends RecursiveTask<Long> { private static final int THRESHOLD = 10000; private long start; private long end; public CountTask(long start,long end){ this.start = start; this.end = end; } @Override protected Long compute() { long sum = 0; boolean canCompute = (end-start)<THRESHOLD; if (canCompute) // 是否可以再分 for (long i = start; i <= end; i++) { sum+=i; } else{ long step = (end+start)/100000; // 分成100個任務 ArrayList<CountTask> tasks = new ArrayList<>(); // 任務數組 long pos = step; // 每個任務的大小 for (int i = 0; i < 100000; i++) { long lastOne = pos+step; // 根據任務大小獲得一個任務 if (lastOne > end) lastOne = end; // 上限超出的話就設定上限 CountTask subTask = new CountTask(pos,lastOne); // 建立一個任務 System.out.println("new task inf : "+pos+"\t"+lastOne); pos+=step+1; // 更新下一個任務的初始位置 tasks.add(subTask); // 將任務都加入到任務數組中 subTask.fork(); // 執行任務 } // 擷取累加值 for (CountTask t : tasks) { sum+=t.join(); } } return sum; } public static void main(String[] args) { ForkJoinPool forkJoinPool = new ForkJoinPool(); // 預設是用cpu的最大核心數去工作 CountTask task = new CountTask(0,200000000L); ForkJoinTask<Long> result = forkJoinPool.submit(task); // 提交一個任務 try { long res = result.get(); System.out.println("sum= "+res); }catch (Exception e){ e.printStackTrace(); } }}