六問六答理解ForkJoin原理

華為雲開發者社群發表於2021-11-10
摘要:ForkJoin執行緒池是將任務分割為子任務,有可能子任務還是很大,還需要進一步拆解,最終得到足夠小的任務。

本文分享自華為雲社群《ForkJoin執行緒池的學習和思考》,作者:breakDraw。

ForkJoin執行緒池在常規的java書籍裡還是提到比較少的,畢竟是java8引入的產物。

首先這裡簡單解釋一下forkJoin的運作原理, 本質上有點像歸併計算。

  1. 他會將提交大任務按照一定規則拆解(fork)成多個小任務
  2. 當任務小到一定程度時,就會執行計算
  3. 執行完成時會和其他的小任務進行合併(join), 逐步將所有小結果合成一個大結果。

六問六答理解ForkJoin原理

可以看這個forkJoinTask的實現虛擬碼,即如果想使用forkJoin併發執行任務,需要自己把任務繼承RecursiveTask,作為forkJoin池的submit物件:

public class ForkJoinTask extends RecursiveTask<任務引數> {
    public ReckonTask(任務引數) {

    } 
 
    @Override
    protected File compute() {
        if(根據任務引數判斷任務是否足夠小) {
        計算,返回
      } else {
           拆分成子任務1和子任務2 
        任務1.fork();
        任務2.fork();
        結果1 = 任務1.join();
        結果2 = 任務2.join();
        返回結果1+結果2;
      }
    }
}

然後實際上整個forkjoin的細節非常多,這裡我通過給自己提好幾個問題,來逐步理解forkJoin的原理:

Q: forkJoin中各個執行緒是如何獲取那些小任務的呢?
A:他是通過工作密取的方式獲取。(java併發那本書裡提到過工作密取workSteal,原來是用在這了)

  • 假設我們給forkJoin設定3個工作執行緒,那麼就會有3個工作佇列, 注意,這個佇列是雙端佇列。
  • 每當執行任務時,如果不滿足小任務的條件,他會fork出2個子任務,並push進自己的工作佇列中。
  • 每個工作執行緒不斷取自己隊頭的任務執行。
  • 關鍵點:如果自己佇列裡沒有資料,則會從其他佇列的隊尾取資料。

Q: fork時具體發生了什麼?
A:是一個非同步的操作, 就是向當前執行緒佇列中新增這個fork出來任務,能放進去的話就返回,不會等待。
注意,預設fork出的任務是先預設給自己的。 當自己做不完時,才可能被別人取走!

六問六答理解ForkJoin原理

Q: join是什麼含義?什麼時候做的?
A:見實現forkJoin任務介面時的程式碼:

六問六答理解ForkJoin原理

可以看到時每次fork完之後, 通過join,來獲取子task的結果,獲取到之後,再合併計算,返回結果。

Q: join這個阻塞過程是怎麼做的?如果把執行緒掛起,那這個執行緒豈不是無法工作了?
A:首先,之前fork時,新的子任務已經被放入佇列了。
每個子任務都有一個任務狀態。
當呼叫該子任務的join時, 會迴圈判斷他的狀態

如果這個子任務狀態未完成, 則從自身佇列或其他人的佇列中取出新的任務執行,因此進入了下一層的exec()操作。

如果發現子任務狀態更新為了完成(這個更新動作可能是自己執行緒完成的,也可能是別的執行緒完成的,反正這個任務的狀態實現了同步和可見), 則將結果返回給上層。

因此join的本質是一個遞迴的過程, 任務沒完成的話,他就取其他任務繼續遞迴往下執行。

更詳細的可以看這個連結fork+join過程詳細解讀

Q: forkJoin存放任務的時候,怎麼保證不會出現併發問題?比如同時往隊尾插入的話
A:

  • n個工作執行緒是通過陣列存放的(即有一個工作執行緒陣列)
  • sun.misc.Unsafe操作類直接基於作業系統控制層在硬體層面上進行原子操作,它是ForkJoinPool高效效能的一大保證,類似的程式設計思路還體現在java.util.concurrent包中相當規模的類功能實現中。

六問六答理解ForkJoin原理

Q: forkJoin應用在哪嗎?
A:java8 stream的parallel併發功能就是基於forkJoin做的, parallelStream實現的forkJoin拆解任務和執行任務的介面, 預設用機器所有CPU數量的forkJoin執行緒池。
如果需要限制執行緒數量,可以用
new forkJoin(執行緒數).submit(()->(list.stream().parallel().map()…)); 即可

 

點選關注,第一時間瞭解華為雲新鮮技術~

相關文章