JDK8并行流及串行流區(qū)別原理詳解
由于處理器核心的增長及較低的硬件成本允許低成本的集群系統(tǒng),致使如今并行編程無處不在,并行編程似乎是下一個大事件。
Java 8 針對這一事實提供了新的 stream API 及簡化了創(chuàng)建并行集合和數(shù)組的代碼。讓我們看一下它是怎么工作的。
假設 myList 是 List<Integer> 類型的,其中包含 500,000 個Integer值。在Java 8 之前的時代中,對這些整數(shù)求和的方法是使用 for 循環(huán)完成的。
for( int i : myList){ result += i; }
從 Java 8 開始,我們就可以使用stream完成同樣的循環(huán):
myList.stream().sum();
將此代碼改為并行處理非常簡單,僅需要使用 parallelStream() 代替 stream() 或 parallel()搭配stream使用:
mylist.stream().parallelStream().sum();
這樣就可以成功的變?yōu)椴⑿谐绦颍詫⒁粋€計算擴展到線程和CPU內核上并可用很容易就可以實現(xiàn)。但是我們都知道,多線程和并行處理的開銷很大,所以重點是什么時候使用并行流,什么時候使用串行流才能獲得更好的性能。
首先,讓我們看看在幕后發(fā)生的事情。parallel stream 使用的是 Fork/Join 框架進行處理的,這意味著 stream 流的源會被拆分并移交給 fork/join 池中執(zhí)行。
首先,我們找到了要考慮的第一點:并非所有的stream的源會像其它的stream的源一樣可拆分。例如:ArrayList的內部實現(xiàn)是數(shù)組,由于可以通過計算出中間元素的索引來拆分,所以拆分這樣的源會非常容易;假如使用LinkedList,則拆分數(shù)據(jù)會復雜的多:該實現(xiàn)必須遍歷第一個條目中的所有元素,以便找到可以拆分的元素,所以LinkedList是并行流中性能差的例子。
這是我們可以保留的關于并行流性能的第一個事實:
S : 源集合必須可以有效拆分
拆分集合、管理 Fork/Join 任務、對象創(chuàng)建及 GC 也是算法上的開銷,當且僅當在CPU核心上可簡單完成或者集合足夠大時,才值得這樣做。
一個錯誤的例子:求5個整數(shù)的最大值。
Intstream.rangeClosed(1,5).reduce(Math::max).getAsInt();
系統(tǒng)為fork/join準備和處理數(shù)據(jù)的開銷非常大,以至于串行流在此場景中要快得多。Math.max 方法在這里的CPU開銷并不是很高,而且數(shù)據(jù)元素很少。
舉個例子,在編寫象棋游戲的時候,對每個棋子移動的評估。每一個評估都可以并行執(zhí)行,并且我們有大量可能的下一步移動。這種情形非常適合并行處理。
這是我們可以保留的關于并行流性能的第二個事實:
N * Q: 因子”元素數(shù)量” * “ 每個元素的運行成本” 應該很大
但這同樣意味著當每個元素的操作成本更高的時候,集合可以更小?;虍斆總€元素的操作不那么占用大量CPU時,我們需要一個包含許多元素的非常大的集合,以便并行流的使用的到回報。
這直接取決于我們可以保留的第三個事實
C :CPU核心數(shù)量 - 越多越好 > 必須有1個
由于管理開銷,在單核計算機上的并行流始終比串行流的性能差。
越多越好:實際上,這句話并不是在所有情況下都正確。例如:集合太小且CPU核心啟動時處于節(jié)能模式進而導致CPU無事可做。
能否使用并行流,對每個元素的功能(function)也有要求,這涉及到并行流能否按照預期工作:
要求該功能(function):
- 獨立:每個元素的計算都不依賴或影響任何其他元素的計算
- 無干擾:功能(function)執(zhí)行的時候不會修改基礎的數(shù)據(jù)源
- 無狀態(tài)
例:并行流中使用有狀態(tài)lamdba方法的實例,來源自 Java JDK API
Set seen = Collection.synchronizedSet(new HashSet()); stream.parallel().map( e -> { if(seen.add(e)) return 0; else return e; })...
于是,這是我們可以保留的第四個事實:
F :每個元素必須獨立
總結:
還有其他情況不應該并行化流嗎?有。
我們要始終考慮每一個元素的功能(function)在做什么及它是否適合運行在并行代碼中。當方法是調用一些同步方法,并行流可能會在同步方法上等待,進而導致并行流的性能并沒有想象中高。
同樣的,在調用BI/O操作時,由于數(shù)據(jù)是按照順序讀取的,以I/O源作為流,也會發(fā)生同樣的問題。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
SpringBoot與spring security的結合的示例
這篇文章主要介紹了SpringBoot與spring security的結合的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03Java動態(tài)代理的兩種實現(xiàn)方式詳解【附相關jar文件下載】
這篇文章主要介紹了Java動態(tài)代理的兩種實現(xiàn)方式,結合實例形式分析了java動態(tài)代理的相關原理、實現(xiàn)方法與操作技巧,并附帶相關jar文件供讀者下載,需要的朋友可以參考下2019-03-03Java數(shù)據(jù)結構之鏈表(動力節(jié)點之Java學院整理)
這篇文章主要介紹了Java數(shù)據(jù)結構之鏈表(動力節(jié)點之Java學院整理)的相關資料,需要的朋友可以參考下2017-04-04IDEA啟動Tomcat時控制臺出現(xiàn)亂碼問題及解決
這篇文章主要介紹了IDEA啟動Tomcat時控制臺出現(xiàn)亂碼問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-02-02SpringBoot 2.6.x整合springfox 3.0報錯問題及解決方案
這篇文章主要介紹了SpringBoot 2.6.x整合springfox 3.0報錯問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01mybatis中關于type-aliases-package的使用
這篇文章主要介紹了mybatis中關于type-aliases-package的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08