ForkJoinPool簡介

ForkJoinPool

背景描述

過去我們在線程池解決問題時筷狼,通常維護了一個阻塞的任務隊列橘忱。每個工作線程在任務完成后,就會去任務隊列里面尋找任務袖订。這種方式在我們執(zhí)行數(shù)量較多且不互相依賴的任務時非常方便且高效慰毅。但是當我們需要執(zhí)行一個很大的任務時隘截,普通的線程池似乎就很難有什么幫助了。

在JDK7中新增了ForkJoinPool汹胃。ForkJoinPool采用分治+work-stealing的思想婶芭。可以讓我們很方便地將一個大任務拆散成小任務着饥,并行地執(zhí)行犀农,提高CPU的使用率。關于ForkJoinPool的精妙之處宰掉,我們將在后面的使用中慢慢說明呵哨。

如何使用

構造方法

Android官方文檔中給出了三個構造方法。我們注意到在構造方法中轨奄,我們可以設置ForkJoinPool的最大工作線程數(shù)孟害、工作線程工廠、拒絕任務的Handler和同步模式挪拟。

執(zhí)行任務

ForkJoinPool提供了兩套執(zhí)行任務的API挨务,它們的區(qū)別主要是返回的結果類型不同。invoke方法返回執(zhí)行的結果,而submit方法返回執(zhí)行的任務谎柄。

使用示例

需求

遍歷系統(tǒng)所有文件丁侄,得到系統(tǒng)中文件的總數(shù)。

思路

通過遞歸的方法朝巫。任務在遍歷中如果發(fā)現(xiàn)文件夾就創(chuàng)建新的任務讓線程池執(zhí)行鸿摇,將返回的文件數(shù)加起來,如果發(fā)現(xiàn)文件則將計數(shù)加一捍歪,最終將該文件夾下的文件數(shù)返回。

代碼實現(xiàn)

    CountingTask countingTask = new CountingTask(Environment.getExternalStorageDirectory());
    forkJoinPool.invoke(countingTask);

    class CountingTask extends RecursiveTask<Integer> {
        private File dir;

        public CountingTask(File dir) {
            this.dir = dir;
        }

        @Override
        protected Integer compute() {
            int count = 0;

            File files[] = dir.listFiles();
            if(files != null){
                for (File f : files){
                    if(f.isDirectory()){
                        // 對每個子目錄都新建一個子任務鸵钝。
                        CountingTask countingTask = new CountingTask(f);
                        countingTask.fork();
                        count += countingTask.join();

                    }else {
                        Log.d("tag" , "current path = "+f.getAbsolutePath());
                        count++;
                    }
                }
            }


            return count;
        }
    }         

原理說明

所謂work-stealing模式糙臼,即每個工作線程都會有自己的任務隊列。當工作線程完成了自己所有的工作后恩商,就會去“偷”別的工作線程的任務变逃。

那么這樣的工作模式,有什么好處呢怠堪?

假如我們需要做一個比較大的任務揽乱,我們可以把這個任務分割為若干互不依賴的子任務,為了減少線程間的競爭粟矿,于是把這些子任務分別放到不同的隊列里凰棉,并為每個隊列創(chuàng)建一個單獨的線程來執(zhí)行隊列里的任務,線程和隊列一一對應陌粹,比如A線程負責處理A隊列里的任務撒犀。但是有的線程會先把自己隊列里的任務干完,而其他線程對應的隊列里還有任務等待處理掏秩。干完活的線程與其等著或舞,不如去幫其他線程干活,于是它就去其他線程的隊列里竊取一個任務來執(zhí)行蒙幻。而在這時它們會訪問同一個隊列映凳,所以為了減少竊取任務線程和被竊取任務線程之間的競爭,通常會使用雙端隊列邮破,被竊取任務線程永遠從雙端隊列的頭部拿任務執(zhí)行诈豌,而竊取任務的線程永遠從雙端隊列的尾部拿任務執(zhí)行。

上面的需求抒和,如果我們用普通的線程池該如何完成队询?

如果我們使用newFixedThreadPool,當核心線程的路徑下都有子文件夾時构诚,它們會將路徑下的子文件夾拋給任務隊列蚌斩,最終變成所有的核心線程都在等待子文件夾的返回結果,從而造成死鎖。最終任務無法完成送膳。

如果我們使用newCachedThreadPool员魏,依然用上面的思路可以完成任務。但是每次子文件夾就會創(chuàng)建一個新的工作線程叠聋,這樣消耗過大撕阎。

因此,在這樣的情況下碌补,ForkJoinPool的work-stealing的方式就體現(xiàn)出了優(yōu)勢虏束。每個任務分配的子任務也由自己執(zhí)行,只有自己的任務執(zhí)行完成時厦章,才會去執(zhí)行別的工作線程的任務镇匀。

再來個例子

N項的Fibonacci數(shù)列求和,我們不再只能仰仗單個線程為我們執(zhí)行任務袜啃。

package com.example;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class MyClass {

    static int computeCount = 0;

    static class Fibonacci extends RecursiveTask<Integer> {
        int n;

        Fibonacci(int n) {
            this.n = n;
        }

        @Override
        protected Integer compute() {
            computeCount ++;
            System.out.printf("Current thread is " + Thread.currentThread()
                    + "\n n = " + n + "\n");

            if (n <= 2)
                return 1;
            Fibonacci f1 = new Fibonacci(n - 1);
            f1.fork();

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            Fibonacci f2 = new Fibonacci(n - 2);
            f2.fork();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("wati temp answer is :" + n + "\n");
            int answer = f1.join() + f2.join();
            System.out.printf("temp answer is :" + answer  + ",  n is :" +n +"\n");
            return answer;
        }
    }


    public static void main(String[] args)  {
        ForkJoinPool pool = new ForkJoinPool(2);
        Fibonacci task = new Fibonacci(5);
        int answer = 0;
        answer = pool.invoke(task);
        System.out.printf("Hello answer is :" + answer +  " , compute count is :" + computeCount);
    }
}

結語

實測下來汗侵,當情況足夠復雜時,ForkJoinPool的優(yōu)勢會愈加明顯群发。但是晰韵,就像快排一樣,最優(yōu)策略并不是一個思路走到死熟妓,當分治的區(qū)域較小時雪猪,可以將小區(qū)域改用插入排序進行排序。同理起愈,當我們遞歸到情況不再復雜時浪蹂,就可以轉而用別的線程池進行處理。

以上

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末告材,一起剝皮案震驚了整個濱河市坤次,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斥赋,老刑警劉巖缰猴,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異疤剑,居然都是意外死亡滑绒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門隘膘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疑故,“玉大人,你說我怎么就攤上這事弯菊∽菔疲” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長钦铁。 經(jīng)常有香客問我软舌,道長,這世上最難降的妖魔是什么牛曹? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任佛点,我火速辦了婚禮,結果婚禮上黎比,老公的妹妹穿的比我還像新娘超营。我一直安慰自己,他們只是感情好阅虫,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布演闭。 她就那樣靜靜地躺著,像睡著了一般书妻。 火紅的嫁衣襯著肌膚如雪船响。 梳的紋絲不亂的頭發(fā)上躬拢,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天躲履,我揣著相機與錄音,去河邊找鬼聊闯。 笑死工猜,一個胖子當著我的面吹牛,可吹牛的內容都是我干的菱蔬。 我是一名探鬼主播篷帅,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拴泌!你這毒婦竟也來了魏身?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蚪腐,失蹤者是張志新(化名)和其女友劉穎箭昵,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體回季,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡家制,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泡一。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颤殴。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鼻忠,靈堂內的尸體忽然破棺而出涵但,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布贤笆,位于F島的核電站蝇棉,受9級特大地震影響,放射性物質發(fā)生泄漏芥永。R本人自食惡果不足惜篡殷,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望埋涧。 院中可真熱鬧板辽,春花似錦、人聲如沸棘催。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽醇坝。三九已至邑跪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間呼猪,已是汗流浹背画畅。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宋距,地道東北人轴踱。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像谚赎,于是被迫代替她去往敵國和親淫僻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內容

  • 一壶唤、多線程 說明下線程的狀態(tài) java中的線程一共有 5 種狀態(tài)雳灵。 NEW:這種情況指的是,通過 New 關鍵字創(chuàng)...
    Java旅行者閱讀 4,680評論 0 44
  • 什么是Fork/Join框架 Fork/Join框架是Java7提供了的一個用于并行執(zhí)行任務的框架闸盔,采用類似于分治...
    碼農(nóng)歷險記閱讀 2,217評論 0 2
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理悯辙,服務發(fā)現(xiàn),斷路器蕾殴,智...
    卡卡羅2017閱讀 134,663評論 18 139
  • 午夜鐘聲響起 夢回唐朝 纖纖女子 柳骨明眉 紅紗拂袖 暗香浮動 輕輕點點 駐足于江湖的風靡 驚心之處 來來往往 觀...
    布爾柯閱讀 227評論 0 1
  • 我們住在同一個城市,我們只有一墻之隔荡灾。我們曾出現(xiàn)在彼此的童年中瓤狐,我們有著相同的喜好和興趣瞬铸,但,那又如何础锐,我們還是經(jīng)...
    蘑小菇_83375閱讀 436評論 0 2