Java之并行流(Parallel Stream)使用詳解
Java并行流(Parallel Stream)
并行流是Java 8引入的高效處理集合數(shù)據(jù)的工具,通過多線程加速計算。
以下是其核心概念、使用方法及注意事項的詳細指南:
1. 核心概念與原理
- 并行處理機制:將數(shù)據(jù)分割為多個塊,利用
Fork/Join
框架在多個線程上并行處理,最后合并結(jié)果。 - 默認線程池:使用
ForkJoinPool.commonPool()
,線程數(shù)等于CPU核心數(shù)(可通過系統(tǒng)參數(shù)調(diào)整)。 - 適用場景:大規(guī)模數(shù)據(jù)集、計算密集型任務(wù)(如數(shù)學(xué)運算、批量轉(zhuǎn)換)。
2. 創(chuàng)建并行流的方式
- 直接生成:通過集合的
parallelStream()
方法。 - 轉(zhuǎn)換順序流:在現(xiàn)有流上調(diào)用
parallel()
。
List<Integer> list = Arrays.asList(1, 2, 3, 4); // 方式1:直接生成并行流 Stream<Integer> parallelStream1 = list.parallelStream(); // 方式2:將順序流轉(zhuǎn)為并行 Stream<Integer> parallelStream2 = list.stream().parallel();
3. 適用場景與性能優(yōu)化
推薦場景:
- 數(shù)據(jù)量大:如百萬級元素的過濾、映射。
- 計算復(fù)雜:如矩陣運算、圖像處理。
- 無狀態(tài)操作:如
map
、filter
、reduce
(不依賴處理順序或外部變量)。
性能陷阱:
- 小數(shù)據(jù)集:并行化開銷(線程調(diào)度、數(shù)據(jù)分割)可能抵消收益。
- 低耗時操作:如簡單加減法,并行可能更慢。
4. 注意事項與最佳實踐
避免共享可變狀態(tài)
并行操作中修改共享變量會導(dǎo)致線程安全問題,應(yīng)使用無狀態(tài)操作或同步控制。
// 錯誤示例:線程不安全的累加 List<Integer> nums = Arrays.asList(1, 2, 3); int[] sum = {0}; nums.parallelStream().forEach(n -> sum += n); // 結(jié)果可能錯誤 // 正確做法:使用歸約 int safeSum = nums.parallelStream().reduce(0, Integer::sum);
謹慎使用有狀態(tài)操作
如sorted()
、distinct()
在并行流中可能更耗時,需合并線程結(jié)果。
// 并行排序(可能比順序流慢) List<Integer> sortedList = nums.parallelStream().sorted().toList();
數(shù)據(jù)源的可拆分性
- 高效結(jié)構(gòu):
ArrayList
、數(shù)組(支持快速隨機訪問,易于分割)。 - 低效結(jié)構(gòu):
LinkedList
、TreeSet
(拆分成本高)。
順序敏感操作
使用forEachOrdered
保證順序,但犧牲性能。
// 按順序輸出(性能低于無序操作) list.parallelStream().forEachOrdered(System.out::println);
配置線程池
默認線程數(shù):
Runtime.getRuntime().availableProcessors()
修改全局線程數(shù):
# JVM啟動參數(shù) -Djava.util.concurrent.ForkJoinPool.common.parallelism=8
5. 性能對比示例
// 順序流 vs 并行流(處理1000萬數(shù)據(jù)) List<Long> numbers = LongStream.rangeClosed(1, 10_000_000) .boxed().collect(Collectors.toList()); // 順序流耗時 long start = System.currentTimeMillis(); long seqSum = numbers.stream().mapToLong(n -> n * 2).sum(); System.out.println("順序流耗時: " + (System.currentTimeMillis() - start) + "ms"); // 并行流耗時 start = System.currentTimeMillis(); long parSum = numbers.parallelStream().mapToLong(n -> n * 2).sum(); System.out.println("并行流耗時: " + (System.currentTimeMillis() - start) + "ms");
典型結(jié)果(8核CPU):
順序流耗時: 120ms 并行流耗時: 35ms
總結(jié)
優(yōu)勢:簡化多線程編程,提升大數(shù)據(jù)處理效率。
局限:不適合小數(shù)據(jù)量、順序敏感或低計算量任務(wù)。
最佳實踐:
- 優(yōu)先處理大規(guī)模數(shù)據(jù)。
- 避免操作共享變量。
- 測試驗證性能提升。
- 使用
forEach
替代forEachOrdered
除非必須保證順序。
通過合理使用并行流,可在不增加復(fù)雜代碼的情況下顯著提升程序性能,但需結(jié)合場景權(quán)衡利弊。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決IDEA報錯,無效的源發(fā)行版 無效的目標發(fā)行版:22問題
在項目編譯過程中,可能會出現(xiàn)“無效的源發(fā)行版”或“無效的目標發(fā)行版”的報錯信息,原因通常是編譯使用的JDK版本與項目設(shè)置的發(fā)布版本不一致,解決這類問題的辦法是統(tǒng)一JDK版本,具體操作為:在IDE的項目設(shè)置中(如File->ProjectStructure->ProjectSettings)2024-10-10SpringBoot+jsp項目啟動出現(xiàn)404的解決方法
這篇文章主要介紹了SpringBoot+jsp項目啟動出現(xiàn)404的解決方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03spring+Jpa多數(shù)據(jù)源配置的方法示例
這篇文章主要介紹了spring+Jpa多數(shù)據(jù)源配置的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-08-08SpringBoot?集成Resteasy實現(xiàn)RESTFul接口的詳細過程
這篇文章主要介紹了SpringBoot集成Resteasy實現(xiàn)RESTFul接口,本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08Spring boot redis cache的key的使用方法
這篇文章主要介紹了Spring boot redis cache的key的使用方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05java實現(xiàn)ModbusCRC16校驗的示例代碼
本文介紹了使用Java實現(xiàn)ModbusCRC16校驗,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-11-11