Java并發(fā)編程之Fork/Join框架詳解
一、簡介
Fork/Join框架是Java7提供的一個用于并行執(zhí)行任務的框架,是一個把大任務分割成若干個小任務,最終匯總每個小任務結(jié)果后得到大任務結(jié)果的框架。
我們再通過Fork和Join這兩個單詞來理解一下Fork/Join框架。
Fork就是把一個大任務切分為若干子任務并行的執(zhí)行,Join就是合并這些子任務的執(zhí)行結(jié)果,最后得到這個大任務的結(jié)果。
比如計算1+2+…+10000,可以分割成10個子任務,每個子任務分別對1000個數(shù)進行求和,最終匯總這10個子任務的結(jié)果。
二、流程
1.分割任務:
首先我們需要有一個fork類來把大任務分割成子任務,有可能子任務還是很大,所以還需要不停地分割,直到分割出的子任務足夠小。
2.執(zhí)行任務并合并結(jié)果。
分割的子任務分別放在雙端隊列里,然后幾個啟動線程分別從雙端隊列里獲取任務執(zhí)行。子任務執(zhí)行完的結(jié)果都統(tǒng)一放在一個隊列里,啟動一個線程從隊列里拿數(shù)據(jù),然后合并這些數(shù)據(jù)。

三、工作竊取算法
工作竊取(work-stealing)算法是指某個線程從其他隊列里竊取任務來執(zhí)行。
假如我們需要做一個比較大的任務,可以把這個任務分割為若干互不依賴的子任務,為了減少線程間的競爭,把這些子任務分別放到不同的隊列里,并為每個隊列創(chuàng)建一個單獨的線程來執(zhí)行隊列里的任務,線程和隊列一一對應。
比如A線程負責處理A隊列里的任務。但是,有的線程會先把自己隊列里的任務干完,而其他線程對應的隊列里還有任務等待處理。
干完活的線程與其等著,不如去幫其他線程干活,于是它就去其他線程的隊列里竊取一個任務來執(zhí)行。
而在這時它們會訪問同一個隊列,所以為了減少竊取任務線程和被竊取任務線程之間的競爭,通常會使用雙端隊列,被竊取任務線程永遠從雙端隊列的頭部拿任務執(zhí)行,而竊取任務的線程永遠從雙端隊列的尾部拿任務執(zhí)行

優(yōu)點:充分利用線程進行并行計算,減少了線程間的競爭。
缺點:在某些情況下還是存在競爭,比如雙端隊列里只有一個任務時。并且該算法會消耗了更多的系統(tǒng)資源,比如創(chuàng)建多個線程和多個雙端隊列。
四、ForkJoin應用
Fork/Join使用兩個類來完成分治的操作
1.ForkJoinTask
我們要使用ForkJoin框架,必須首先創(chuàng)建一個ForkJoin任務。它提供在任務中執(zhí)行fork()和join()操作的機制。通常情況下,我們不需要直接繼承ForkJoinTask類,只需要繼承它的子類,F(xiàn)ork/Join框架提供了以下兩個子類。
·RecursiveAction:用于沒有返回結(jié)果的任務。·RecursiveTask:用于有返回結(jié)果的任務核心方法:
fork():在當前線程運行的線程池中創(chuàng)建一個子任務;join():模塊子任務完成的時候返回任務結(jié)果;invoke():執(zhí)行任務,也可以實時等待最終執(zhí)行結(jié)果
2.ForkJoinPool
線程池最大的特點就是分叉(fork)合并(join)模式,將一個大任務拆分成多個小任務,并行執(zhí)行,再結(jié)合工作竊取算法提高整體的執(zhí)行效率,充分利用CPU資源。
3.實例
private static final Integer MAX = 200;
static class MyForkJoinTask extends RecursiveTask<Integer> {
// 子任務開始計算的值
private Integer startValue;
// 子任務結(jié)束計算的值
private Integer endValue;
public MyForkJoinTask(Integer startValue , Integer endValue) {
this.startValue = startValue;
this.endValue = endValue;
}
@Override
protected Integer compute() {
// 如果條件成立,說明這個任務所需要計算的數(shù)值分為足夠小了
// 可以正式進行累加計算了
if(endValue - startValue < MAX) {
System.out.println("開始計算的部分:startValue = " + startValue + ";endValue = " + endValue);
Integer totalValue = 0;
for(int index = this.startValue ; index <= this.endValue ; index++) {
totalValue += index;
}
return totalValue;
}
// 否則再進行任務拆分,拆分成兩個任務
else {
MyForkJoinTask subTask1 = new MyForkJoinTask(startValue, (startValue + endValue) / 2);
subTask1.fork();
MyForkJoinTask subTask2 = new MyForkJoinTask((startValue + endValue) / 2 + 1 , endValue);
subTask2.fork();
return subTask1.join() + subTask2.join();
}
}
}
public static void main(String[] args) {
// 這是Fork/Join框架的線程池
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> taskFuture = pool.submit(new MyForkJoinTask(1,1001));
try {
Integer result = taskFuture.get();
System.out.println("result = " + result);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace(System.out);
}
}到此這篇關(guān)于Java并發(fā)編程之Fork/Join框架詳解的文章就介紹到這了,更多相關(guān)Fork/Join框架內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis返回int或者Integer類型報錯的解決辦法
這篇文章主要介紹了Mybatis返回int或者Integer類型報錯的解決辦法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-12-12
Spring使用Configuration注解管理bean的方式詳解
在Spring的世界里,Configuration注解就像是一位細心的園丁,它的主要職責是在這個繁花似錦的園子里,幫助我們聲明和管理各種各樣的bean,本文給大家介紹了在Spring中如何優(yōu)雅地管理你的bean,需要的朋友可以參考下2024-05-05
SpringBoot整合jnotify實現(xiàn)針對指定目錄及其(動態(tài))子目錄的監(jiān)聽的方法
本文介紹了JNotify這一Java庫在SpringBoot中的應用,JNotify允許應用程序監(jiān)聽文件系統(tǒng)事件,包括文件夾/文件的創(chuàng)建、刪除、修改和重命名,由于JNotify底層調(diào)用的關(guān)鍵部分是C語言開發(fā)的,所以在使用前需要在系統(tǒng)中加入相應的動態(tài)庫2024-10-10
解決mybatisplus插入報錯argument type mismatch的問題
這篇文章主要介紹了解決mybatisplus插入報錯argument type mismatch的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11
使用@CachePut?更新數(shù)據(jù)庫和更新緩存
這篇文章主要介紹了使用@CachePut?更新數(shù)據(jù)庫和更新緩存方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12

