文章目錄
前言
ForkJoinPool
作爲線程池,ForkJoinTask
爲任務,ForkJoinWorkerThread
作爲執行任務的線程,三者構成的任務調度機制在 Java 中通常被稱爲Fork/Join 框架
ForkJoin
框架在 Java 7 的時候就加入到了 Java 併發包 java.util.concurrent
,並且在 Java 8 的 lambda 並行流
中充當着底層框架的角色。其主要實現的功能是採用“分而治之”的算法將一個大型複雜任務 fork()分解
成足夠小的任務才使用多線程去並行處理這些小任務,處理完得到各個小任務的執行結果後再進行join()合併
,將其彙集成一個大任務的結果,最終得到最初提交的那個大型複雜任務的執行結果
ForkJoin框架
最適合的是計算密集型的任務,如果存在 I/O,線程間同步,sleep() 等會造成線程長時間阻塞的情況時會影響整體效率,此時可配合使用 ManagedBlocker
。ForkJoinPool
線程池爲了提高任務的並行度和吞吐量做了很多複雜的設計實現,目的是充分利用CPU,其中最著名的就是 work stealing
任務竊取機制
ManagedBlocker
相當於明確告訴ForkJoinPool
有個任務要阻塞了,ForkJoinPool
就會啓用另一個線程來運行任務,以最大化地利用CPU
1. ForkJoinPool 的組件
1.1 線程池 ForkJoinPool
ForkJoinPool
的繼承體系如下,可以看到它和 ThreadPoolExecutor
一樣都是繼承自AbstractExecutorService
抽象類,所以它和 ThreadPoolExecutor
的使用幾乎沒什麼區別,只是任務對象變成了 ForkJoinTask
1.1.1 ForkJoinPool 線程池的創建
與其他線程池類似,ForkJoinPool
線程池可使用以下方式創建
Executors.newWorkStealingPool()
public static ExecutorService newWorkStealingPool(int parallelism) { return new ForkJoinPool (parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); }
構造方法中各個參數的含義如下:
parallelism
: 並行度
即配置線程池線程個數,如果沒有指定,則默認爲Runtime.getRuntime().availableProcessors() - 1
,最大值不能超過MAX_CAP =32767
,可通過以下方式自定義配置System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "8"); // 或者啓動參數指定 -Djava.util.concurrent.ForkJoinPool.common.parallelism=8
factory
: 工作線程創建工廠handler
: 未捕獲異常的處理器類
多線程中子線程拋出的異常是無法被主線程 catch 的,因此需要使用UncaughtExceptionHandler
對異常進行統一處理asyncMode
: 任務隊列出隊模式
默認爲false
,使用 LIFO(後入先出,類似棧結構)策略處理任務;爲true
時處理策略是 FIFO(先進先出),更接近於一個消息隊列,不適用於處理遞歸式的任務
1.1.2 ForkJoinPool 線程池重要屬性
// 配合ctl在控制線程數量時使用
private static final long ADD_WORKER = 0x0001L << (TC_SHIFT + 15);
// 控制ForkJoinPool創建線程數量,(ctl & ADD_WORKER) != 0L 時創建線程,也就是當ctl的第16位不爲0時,可以繼續創建線程
volatile long ctl; // main pool control
// 全局鎖控制,全局運行狀態
volatile int runState; // lockable status
// 低16位表示記錄並行數量,高16位表示 ForkJoinPool 處理任務的模式(異步/同步)
final int config; // parallelism, mode
// 工作任務隊列數組
volatile WorkQueue[] workQueues; // main registry
// 默認線程工廠,默認實現是DefaultForkJoinWorkerThreadFactory
final ForkJoinWorkerThreadFactory factory;
1.2 工作線程 ForkJoinWorkerThread
1.3 線程任務 ForkJoinTask
2. 任務執行流程
3. work stealing 算法
(1)每個工作線程都有自己的工作隊列WorkQueue;
(2)這是一個雙端隊列,它是線程私有的;
(3)ForkJoinTask中fork的子任務,將放入運行該任務的工作線程的隊頭,工作線程將以LIFO的順序來處理工作隊列中的任務;
(4)爲了最大化地利用CPU,空閒的線程將從其它線程的隊列中“竊取”任務來執行;
(5)從工作隊列的尾部竊取任務FIFO,以減少競爭;
(6)雙端隊列的操作:push()/pop()僅在其所有者工作線程中調用,poll()是由其它線程竊取任務時調用的;
(7)當只剩下最後一個任務時,還是會存在競爭,是通過CAS來實現的;