Java 8 并行流(Parallel Stream) 介紹

這篇博客,我們將介紹Java中的并行流(Parallel Stream)。

Java 8引入了并行流的概念來(lái)進(jìn)行并行處理。 隨著技術(shù)的發(fā)展,我們所使用的計(jì)算機(jī)硬件成本越來(lái)越廉價(jià)枢析,性能卻遵循者摩爾定律越來(lái)越快。無(wú)論是大型服務(wù)器還是個(gè)人計(jì)算機(jī)刃麸,多核CPU已不再是觸不可及的稀缺資源醒叁,因此可以使用并行處理來(lái)充分榨取CPU資源獲取更好的性能。

下面讓我們通過(guò)2個(gè)例子來(lái)做個(gè)對(duì)比以更好的了解并行技術(shù)

public class Java8ParallelStreamMain {
    public static void main(String[] args) {
        System.out.println("=================================");
        System.out.println("Using Sequential Stream");
        System.out.println("=================================");
        int[] array= {1,2,3,4,5,6,7,8,9,10};
        IntStream intArrStream=Arrays.stream(array);
        intArrStream.forEach(s->
        {
            System.out.println(s+" "+Thread.currentThread().getName());
        }
                );
        System.out.println("=================================");
        System.out.println("Using Parallel Stream");
        System.out.println("=================================");
        IntStream intParallelStream=Arrays.stream(array).parallel();
        intParallelStream.forEach(s->
        {
            System.out.println(s+" "+Thread.currentThread().getName());
        }
                );
    }
}

在自己的電腦上執(zhí)行上述代碼泊业,你會(huì)看到控制臺(tái)的輸出如下:

=================================
Using Sequential Stream
=================================
1 main
2 main
3 main
4 main
5 main
6 main
7 main
8 main
9 main
10 main
=================================
Using Parallel Stream
=================================
7 main
6 main
8 ForkJoinPool.commonPool-worker-3
2 main
1 ForkJoinPool.commonPool-worker-3
5 ForkJoinPool.commonPool-worker-3
4 ForkJoinPool.commonPool-worker-5
9 ForkJoinPool.commonPool-worker-2
3 ForkJoinPool.commonPool-worker-1
10 ForkJoinPool.commonPool-worker-4

我們可以看到控制臺(tái)的輸出把沼,在順序執(zhí)行的情況下主線程(main thread)會(huì)完成所有工作。所有的工作串行執(zhí)行吁伺,直到等到當(dāng)前迭代執(zhí)行完成饮睬,才會(huì)進(jìn)行下一個(gè)迭代。

同樣篮奄,我們注意到在并行流的情況下捆愁,會(huì)同時(shí)生成6個(gè)線程,并在內(nèi)部使用Fork和Join池創(chuàng)建和管理線程窟却。并行流通過(guò)靜態(tài)ForkJoinPool.commonPool() 方法創(chuàng)建ForkJoinPool實(shí)例昼丑。

并行流(Parallel Stream)利用所有可用CPU內(nèi)核的優(yōu)勢(shì),并并行處理任務(wù)夸赫。 如果任務(wù)數(shù)超過(guò)內(nèi)核數(shù)菩帝,則其余任務(wù)將等待當(dāng)前正在運(yùn)行的任務(wù)完成。
你可以通過(guò) Runtime.getRuntime().availableProcessors()來(lái)獲取當(dāng)前計(jì)算機(jī)的CPU 內(nèi)核數(shù)量。

并行流優(yōu)勢(shì)如此大呼奢,那么我們應(yīng)該始終使用它嗎宜雀?
答案是否定的,
通過(guò)上述代碼我們可以看到僅通過(guò)添加parallel()即可輕松將順序流轉(zhuǎn)換為并行流控妻,但是這并不意味著我們應(yīng)該始終使用它州袒。
在使用并行流時(shí)我們需要考慮很多因素揭绑,否則我們將會(huì)遭受并行流所帶來(lái)的負(fù)面影響弓候。

并行流比順序流具有更高的開銷,并且在線程之間的上下文進(jìn)行協(xié)調(diào)切換需要花費(fèi)大量時(shí)間他匪。
僅在以下情況下菇存,才需要考慮是否使用并行流:

  • 需要處理大量數(shù)據(jù)集。
  • 我們知道 Java 使用ForkJoinPool實(shí)現(xiàn)并行性邦蜜,ForkJoinPool派生源流并提交執(zhí)行依鸥,因此源數(shù)據(jù)流應(yīng)該是可拆分的。例如:ArrayList非常容易拆分悼沈,因?yàn)槲覀兛梢酝ㄟ^(guò)索引找到中間元素并將其拆分贱迟,但是LinkedList很難拆分,并且在大多數(shù)情況下表現(xiàn)不佳絮供。在這種情況下就不適合用并行流衣吠。
  • 我們?cè)谔幚韱栴}的時(shí)候確實(shí)遇到流性能問題,否則請(qǐng)不要為了并行而并行壤靶。
  • 我們需要確保線程之間的所有共享資源都是正確同步缚俏,否則可能會(huì)產(chǎn)生數(shù)據(jù)不一致問題。

衡量并行度的最簡(jiǎn)單公式是Brian Goetz在其演講中提到的NQ模型贮乳。

N x Q >10000
N = 數(shù)據(jù)集中的項(xiàng)目數(shù)量
Q = 每個(gè)項(xiàng)目的工作量

這意味著如果您有大量數(shù)據(jù)集并且每個(gè)項(xiàng)目的工作量比較少(例如:求和)忧换,那么并行性可能會(huì)幫助您提升程序性能,反之亦然向拆。 因此亚茬,如果您有較少的數(shù)據(jù)集和每個(gè)項(xiàng)目更多的工作(做一些計(jì)算工作),那么并行性也可以幫助您提升程序性能浓恳。

下面才写,讓我們看一個(gè)相對(duì)復(fù)雜一點(diǎn)的用例。
即將展示的這個(gè)示例中奖蔓,我們將非常明顯的看到在并行流和順序流的情況下赞草,程序執(zhí)行的時(shí)長(zhǎng)和CPU的行為。

public class PerformanceComparisonMain {
    public static void main(String[] args) {
        long currentTime=System.currentTimeMillis();
        List<Integer> data=new ArrayList<Integer>();
        for (int i = 0; i < 100000; i++) {
            data.add(i);
        }       
        long sum=data.stream()
                .map(i ->(int)Math.sqrt(i))
                .map(number->performComputation(number))
                .reduce(0,Integer::sum);
        
        System.out.println(sum);
        long endTime=System.currentTimeMillis();
        System.out.println("Time taken to complete:"+(endTime-currentTime)/(1000*60)+" minutes");
        
    }
    
    public static int performComputation(int number)
    {
        int sum=0;
        for (int i = 1; i < 1000000; i++) {
            int div=(number/i);
            sum+=div;
            
        }
        return sum;
    }
}

當(dāng)執(zhí)行上述代碼吆鹤,控制臺(tái)輸出如下:

117612733
Time taken to complete:5 minutes

上述代碼執(zhí)行時(shí)長(zhǎng)與我們電腦的CPU密切相關(guān)厨疙,我們來(lái)看看在執(zhí)行代碼過(guò)程中CPU的行為

Parallel Stream.png

如您所見,在順序流的情況下疑务,CPU沒有得到充分利用沾凄。

讓我們更改一下上面的代碼梗醇,在8行加上.parallel() 并行處理。

long sum=data.stream()
                .parallel()
                .map(i ->(int)Math.sqrt(i))
                .map(number->performComputation(number))
                .reduce(0,Integer::sum);

再次執(zhí)行上述代碼撒蟀,您將會(huì)看到如下控制臺(tái)輸出

117612733
Time taken to complete:1 minutes

讓我們?cè)俅蝸?lái)看看在并行執(zhí)行的情況下CPU的運(yùn)行情況

Parallel Stream.png

綜上對(duì)比我們發(fā)現(xiàn)在并行執(zhí)行的情況下程序的執(zhí)行性能提升了將近5倍叙谨。前面我們已經(jīng)提到,并行流底層實(shí)現(xiàn)采用的ForkJoin機(jī)制保屯,關(guān)于Fork-Join的更多細(xì)節(jié)手负,下一篇博文呈現(xiàn)給大家。

祝您編碼愉快~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末姑尺,一起剝皮案震驚了整個(gè)濱河市竟终,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌切蟋,老刑警劉巖统捶,帶你破解...
    沈念sama閱讀 216,544評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柄粹,居然都是意外死亡喘鸟,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門驻右,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)什黑,“玉大人,你說(shuō)我怎么就攤上這事旺入《以洌” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵茵瘾,是天一觀的道長(zhǎng)礼华。 經(jīng)常有香客問我,道長(zhǎng)拗秘,這世上最難降的妖魔是什么圣絮? 我笑而不...
    開封第一講書人閱讀 58,193評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮雕旨,結(jié)果婚禮上扮匠,老公的妹妹穿的比我還像新娘。我一直安慰自己凡涩,他們只是感情好棒搜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著活箕,像睡著了一般力麸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評(píng)論 1 299
  • 那天克蚂,我揣著相機(jī)與錄音闺鲸,去河邊找鬼。 笑死埃叭,一個(gè)胖子當(dāng)著我的面吹牛摸恍,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播赤屋,決...
    沈念sama閱讀 40,063評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼立镶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了益缎?” 一聲冷哼從身側(cè)響起谜慌,我...
    開封第一講書人閱讀 38,917評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤然想,失蹤者是張志新(化名)和其女友劉穎莺奔,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體变泄,經(jīng)...
    沈念sama閱讀 45,329評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡令哟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妨蛹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屏富。...
    茶點(diǎn)故事閱讀 39,722評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蛙卤,靈堂內(nèi)的尸體忽然破棺而出狠半,到底是詐尸還是另有隱情,我是刑警寧澤颤难,帶...
    沈念sama閱讀 35,425評(píng)論 5 343
  • 正文 年R本政府宣布神年,位于F島的核電站,受9級(jí)特大地震影響行嗤,放射性物質(zhì)發(fā)生泄漏已日。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評(píng)論 3 326
  • 文/蒙蒙 一栅屏、第九天 我趴在偏房一處隱蔽的房頂上張望飘千。 院中可真熱鬧,春花似錦栈雳、人聲如沸护奈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)霉旗。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奖慌,已是汗流浹背抛虫。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留简僧,地道東北人建椰。 一個(gè)月前我還...
    沈念sama閱讀 47,729評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像岛马,于是被迫代替她去往敵國(guó)和親棉姐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評(píng)論 2 353

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