Java并發(fā)編程-分而治之:Fork&Join線(xiàn)程池


參考資料:《實(shí)戰(zhàn)Java高并發(fā)程序設(shè)計(jì)》


1. 簡(jiǎn)介

  • 分而治之是一個(gè)非常有效的處理大量數(shù)據(jù)的方法。

  • Fork一詞原義是吃飯用的叉子,也有分叉的意思橘茉。Linux中使用fork()函數(shù)來(lái)創(chuàng)建子進(jìn)程,從而使系統(tǒng)進(jìn)程可以多一個(gè)執(zhí)行分支。Java中也沿用了類(lèi)似的命名映跟。

  • Join的含義和Thread.join()類(lèi)似,表示等待扬虚。也就是使用fork()后系統(tǒng)多了一個(gè)執(zhí)行分支(線(xiàn)程)努隙,所以需要等待這個(gè)線(xiàn)程執(zhí)行完畢,才有可能得到最終結(jié)果辜昵。

  • 在實(shí)際使用中荸镊,如果毫無(wú)顧忌地使用fork()開(kāi)啟線(xiàn)程,那么很有可能導(dǎo)致系統(tǒng)因開(kāi)啟過(guò)多線(xiàn)程而嚴(yán)重影響性能。所以JDK給出了一個(gè)ForkJoinPool線(xiàn)程池躬存,對(duì)于fork方法并不急著開(kāi)啟線(xiàn)程张惹,而是提交給ForkJoinPool線(xiàn)程池處理,以節(jié)省系統(tǒng)資源岭洲。

  • 使用Fork&Join進(jìn)行進(jìn)行數(shù)據(jù)處理的總體結(jié)構(gòu)如下:


    forkJoin.png-208.1kB
    forkJoin.png-208.1kB
  • 由于線(xiàn)程池的優(yōu)化宛逗,提交的任務(wù)和線(xiàn)程數(shù)量并不是一對(duì)一的關(guān)系。在絕大多數(shù)情況下盾剩,一個(gè)物理線(xiàn)程實(shí)際上是需要處理多個(gè)邏輯任務(wù)的雷激。因此,每個(gè)線(xiàn)程必然需要擁有一個(gè)任務(wù)隊(duì)列告私。所以屎暇,可能遇到這樣一種情況:線(xiàn)程A已經(jīng)把自己的任務(wù)都執(zhí)行完成了,而線(xiàn)程B還有一堆任務(wù)等著處理德挣。此時(shí)恭垦,線(xiàn)程A就會(huì)“幫助”線(xiàn)程B,從線(xiàn)程B的任務(wù)隊(duì)列中拿一個(gè)任務(wù)過(guò)來(lái)處理格嗅,盡可能地達(dá)到平衡番挺。示意圖:

    queue.png-63.8kB
    queue.png-63.8kB

  • 值得注意的是:當(dāng)一個(gè)線(xiàn)程試圖幫助另一個(gè)線(xiàn)程時(shí),總是從任務(wù)隊(duì)列的尾部拿數(shù)據(jù)屯掖,而線(xiàn)程試圖執(zhí)行自己的任務(wù)時(shí)玄柏,則是從頭部開(kāi)始拿。這是為了避免數(shù)據(jù)競(jìng)爭(zhēng)贴铜。

2.ForkJoinPool 和 ForkJoinTask

  • 先來(lái)看一下ForkJoinPool的一個(gè)重要的接口:
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)
  • 我們可以向ForkJoinPool線(xiàn)程池提交一個(gè)ForkJoinTask任務(wù)粪摘。
  • 所謂ForkJoinTask任務(wù)就是支持fork()分解join()等待的任務(wù)。
  • ForkJoinTask有兩個(gè)重要的子類(lèi):
  1. RecursiveAction沒(méi)有返回值的任務(wù)
  2. RecursiveTask攜帶返回值的任務(wù)
  • 下面通過(guò)一個(gè)計(jì)算數(shù)列求和的demo绍坝,來(lái)展示Fork&Join的使用:
public class Test {

    public static 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 {
                // 分成100個(gè)小任務(wù)
                long step = (start + end) / 100;
                ArrayList<CountTask> subTasks = new ArrayList<>();
                long pos = start;
                for (int i = 0; i < 100; i++) {
                    long lastOne = pos + step;
                    if (lastOne > end) {
                        lastOne = end;
                    }
                    CountTask subTask = new CountTask(pos, lastOne);
                    pos += step + 1;
                    subTasks.add(subTask);
                    subTask.fork();
                }
                for (CountTask t : subTasks) {
                    sum += t.join();
                }
            }
            return sum;
        }
    }

    public static void main(String[] args) throws Exception {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = forkJoinPool.submit(new CountTask(0, 200000L));
        try {
            long result = task.get();
            System.out.println("sum=" + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

// 輸出:
// sum=20000100000
  • 上述代碼在執(zhí)行g(shù)et()方法時(shí)徘意,如果任務(wù)沒(méi)有結(jié)束,那么主線(xiàn)程就會(huì)在get()方法上等待轩褐。
  • 使用ForkJoin時(shí)要注意椎咧,如果任務(wù)的劃分層次很深,一直得不到返回把介,那么可能出現(xiàn)兩種情況:
  1. 系統(tǒng)內(nèi)的線(xiàn)程數(shù)量越積越多勤讽,導(dǎo)致性能?chē)?yán)重下降。
  2. 函數(shù)的調(diào)用層次變得很深拗踢,最終導(dǎo)致棧溢出脚牍。
  • 此外,F(xiàn)orkJoin線(xiàn)程池使用一個(gè)無(wú)鎖的棧來(lái)管理空閑線(xiàn)程巢墅。如果一個(gè)工作線(xiàn)程暫時(shí)取不到可用的任務(wù)诸狭,則可能會(huì)被掛起券膀,掛起的線(xiàn)程將會(huì)被壓入由線(xiàn)程池維護(hù)的棧中。待將來(lái)有任務(wù)可用時(shí)作谚,再?gòu)?strong>棧中喚醒這些線(xiàn)程三娩。

end

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庵芭,一起剝皮案震驚了整個(gè)濱河市妹懒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌双吆,老刑警劉巖眨唬,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異好乐,居然都是意外死亡匾竿,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén)蔚万,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)岭妖,“玉大人,你說(shuō)我怎么就攤上這事反璃£腔牛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵淮蜈,是天一觀(guān)的道長(zhǎng)斋攀。 經(jīng)常有香客問(wèn)我,道長(zhǎng)梧田,這世上最難降的妖魔是什么淳蔼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮裁眯,結(jié)果婚禮上鹉梨,老公的妹妹穿的比我還像新娘。我一直安慰自己穿稳,他們只是感情好存皂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著司草,像睡著了一般艰垂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上埋虹,一...
    開(kāi)封第一講書(shū)人閱讀 49,764評(píng)論 1 290
  • 那天猜憎,我揣著相機(jī)與錄音,去河邊找鬼搔课。 笑死胰柑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播柬讨,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼崩瓤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了踩官?” 一聲冷哼從身側(cè)響起却桶,我...
    開(kāi)封第一講書(shū)人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔗牡,沒(méi)想到半個(gè)月后颖系,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辩越,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年嘁扼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片黔攒。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡趁啸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出督惰,到底是詐尸還是另有隱情不傅,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布姑丑,位于F島的核電站蛤签,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏栅哀。R本人自食惡果不足惜震肮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望留拾。 院中可真熱鬧戳晌,春花似錦、人聲如沸痴柔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)咳蔚。三九已至豪嚎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谈火,已是汗流浹背侈询。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糯耍,地道東北人扔字。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓囊嘉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親革为。 傳聞我的和親對(duì)象是個(gè)殘疾皇子扭粱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本文是我自己在秋招復(fù)習(xí)時(shí)的讀書(shū)筆記,整理的知識(shí)點(diǎn)震檩,也是為了防止忘記琢蛤,尊重勞動(dòng)成果,轉(zhuǎn)載注明出處哦恳蹲!如果你也喜歡虐块,那...
    波波波先森閱讀 11,242評(píng)論 4 56
  • 在這篇文章中俩滥,將覆蓋如下內(nèi)容: 什么是Fork/Join框架 工作竊取算法 Fork/Join框架的設(shè)計(jì) Recu...
    打鐵大師閱讀 740評(píng)論 0 2
  • JAVA并發(fā)編程與高并發(fā)解決方案 - 并發(fā)編程 五 相關(guān)文章 JAVA并發(fā)編程與高并發(fā)解決方案 - 并發(fā)編程 一 ...
    chuIllusions丶閱讀 1,713評(píng)論 0 7
  • 進(jìn)程和線(xiàn)程 進(jìn)程 所有運(yùn)行中的任務(wù)通常對(duì)應(yīng)一個(gè)進(jìn)程,當(dāng)一個(gè)程序進(jìn)入內(nèi)存運(yùn)行時(shí),即變成一個(gè)進(jìn)程.進(jìn)程是處于運(yùn)行過(guò)程中...
    勝浩_ae28閱讀 5,087評(píng)論 0 23
  • 曾經(jīng)我以為我找到了我的蓋世英雄嘉蕾,找到愛(ài)的港灣∷桑可以為我遮住風(fēng)雨错忱,讓我有一個(gè)棲息之所」揖荩可是后來(lái)我發(fā)現(xiàn)這些風(fēng)雨似乎是和...
    愛(ài)自己2023閱讀 412評(píng)論 0 0