這篇文章主要介紹了通過(guò)實(shí)例解析java8中的parallelStream,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
創(chuàng)新互聯(lián)專注于東鄉(xiāng)企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè),成都商城網(wǎng)站開(kāi)發(fā)。東鄉(xiāng)網(wǎng)站建設(shè)公司,為東鄉(xiāng)等地區(qū)提供建站服務(wù)。全流程按需設(shè)計(jì),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
about Stream
什么是流?
Stream是java8中新增加的一個(gè)特性,被java猿統(tǒng)稱為流.
Stream 不是集合元素,它不是數(shù)據(jù)結(jié)構(gòu)并不保存數(shù)據(jù),它是有關(guān)算法和計(jì)算的,它更像一個(gè)高級(jí)版本的 Iterator。原始版本的 Iterator,用戶只能顯式地一個(gè)一個(gè)遍歷元素并對(duì)其執(zhí)行某些操作;高級(jí)版本的 Stream,用戶只要給出需要對(duì)其包含的元素執(zhí)行什么操作,比如 “過(guò)濾掉長(zhǎng)度大于 10 的字符串”、“獲取每個(gè)字符串的首字母”等,Stream 會(huì)隱式地在內(nèi)部進(jìn)行遍歷,做出相應(yīng)的數(shù)據(jù)轉(zhuǎn)換。
Stream 就如同一個(gè)迭代器(Iterator),單向,不可往復(fù),數(shù)據(jù)只能遍歷一次,遍歷過(guò)一次后即用盡了,就好比流水從面前流過(guò),一去不復(fù)返。
而和迭代器又不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顧名思義,當(dāng)使用串行方式去遍歷時(shí),每個(gè) item 讀完后再讀下一個(gè) item。而使用并行去遍歷時(shí),數(shù)據(jù)會(huì)被分成多個(gè)段,其中每一個(gè)都在不同的線程中處理,然后將結(jié)果一起輸出。Stream 的并行操作依賴于 Java7 中引入的 Fork/Join 框架(JSR166y)來(lái)拆分任務(wù)和加速處理過(guò)程。Java 的并行 API 演變歷程基本如下:
Stream 的另外一大特點(diǎn)是,數(shù)據(jù)源本身可以是無(wú)限的。
parallelStream是什么
parallelStream其實(shí)就是一個(gè)并行執(zhí)行的流.它通過(guò)默認(rèn)的ForkJoinPool,可能提高你的多線程任務(wù)的速度.
parallelStream的作用
Stream具有平行處理能力,處理的過(guò)程會(huì)分而治之,也就是將一個(gè)大任務(wù)切分成多個(gè)小任務(wù),這表示每個(gè)任務(wù)都是一個(gè)操作,因此像以下的程式片段:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); numbers.parallelStream() .forEach(out::println);
你得到的展示順序不一定會(huì)是1、2、3、4、5、6、7、8、9,而可能是任意的順序,就forEach()這個(gè)操作來(lái)講,如果平行處理時(shí),希望最后順序是按照原來(lái)Stream的數(shù)據(jù)順序,那可以調(diào)用forEachOrdered()。例如:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); numbers.parallelStream() .forEachOrdered(out::println);
注意:如果forEachOrdered()中間有其他如filter()的中介操作,會(huì)試著平行化處理,然后最終forEachOrdered()會(huì)以原數(shù)據(jù)順序處理,因此,使用forEachOrdered()這類的有序處理,可能會(huì)(或完全失去)失去平行化的一些優(yōu)勢(shì),實(shí)際上中介操作亦有可能如此,例如sorted()方法。
parallelStream背后的男人:ForkJoinPool
要想深入的研究parallelStream之前,那么我們必須先了解ForkJoin框架和ForkJoinPool.本文旨在parallelStream,但因?yàn)閮煞N關(guān)系甚密,故在此簡(jiǎn)單介紹一下ForkJoinPool,如有興趣可以更深入的去了解下ForkJoin***(當(dāng)然,如果你想真正的搞透parallelStream,那么你依然需要先搞透ForkJoinPool).*
ForkJoin框架是從jdk7中新特性,它同ThreadPoolExecutor一樣,也實(shí)現(xiàn)了Executor和ExecutorService接口。它使用了一個(gè)無(wú)限隊(duì)列來(lái)保存需要執(zhí)行的任務(wù),而線程的數(shù)量則是通過(guò)構(gòu)造函數(shù)傳入,如果沒(méi)有向構(gòu)造函數(shù)中傳入希望的線程數(shù)量,那么當(dāng)前計(jì)算機(jī)可用的CPU數(shù)量會(huì)被設(shè)置為線程數(shù)量作為默認(rèn)值。
ForkJoinPool主要用來(lái)使用分治法(Divide-and-Conquer Algorithm)來(lái)解決問(wèn)題。典型的應(yīng)用比如快速排序算法。這里的要點(diǎn)在于,F(xiàn)orkJoinPool需要使用相對(duì)少的線程來(lái)處理大量的任務(wù)。比如要對(duì)1000萬(wàn)個(gè)數(shù)據(jù)進(jìn)行排序,那么會(huì)將這個(gè)任務(wù)分割成兩個(gè)500萬(wàn)的排序任務(wù)和一個(gè)針對(duì)這兩組500萬(wàn)數(shù)據(jù)的合并任務(wù)。以此類推,對(duì)于500萬(wàn)的數(shù)據(jù)也會(huì)做出同樣的分割處理,到最后會(huì)設(shè)置一個(gè)閾值來(lái)規(guī)定當(dāng)數(shù)據(jù)規(guī)模到多少時(shí),停止這樣的分割處理。比如,當(dāng)元素的數(shù)量小于10時(shí),會(huì)停止分割,轉(zhuǎn)而使用插入排序?qū)λ鼈冞M(jìn)行排序。那么到最后,所有的任務(wù)加起來(lái)會(huì)有大概2000000+個(gè)。問(wèn)題的關(guān)鍵在于,對(duì)于一個(gè)任務(wù)而言,只有當(dāng)它所有的子任務(wù)完成之后,它才能夠被執(zhí)行。
所以當(dāng)使用ThreadPoolExecutor時(shí),使用分治法會(huì)存在問(wèn)題,因?yàn)門hreadPoolExecutor中的線程無(wú)法像任務(wù)隊(duì)列中再添加一個(gè)任務(wù)并且在等待該任務(wù)完成之后再繼續(xù)執(zhí)行。而使用ForkJoinPool時(shí),就能夠讓其中的線程創(chuàng)建新的任務(wù),并掛起當(dāng)前的任務(wù),此時(shí)線程就能夠從隊(duì)列中選擇子任務(wù)執(zhí)行。
那么使用ThreadPoolExecutor或者ForkJoinPool,會(huì)有什么性能的差異呢?
首先,使用ForkJoinPool能夠使用數(shù)量有限的線程來(lái)完成非常多的具有父子關(guān)系的任務(wù),比如使用4個(gè)線程來(lái)完成超過(guò)200萬(wàn)個(gè)任務(wù)。但是,使用ThreadPoolExecutor時(shí),是不可能完成的,因?yàn)門hreadPoolExecutor中的Thread無(wú)法選擇優(yōu)先執(zhí)行子任務(wù),需要完成200萬(wàn)個(gè)具有父子關(guān)系的任務(wù)時(shí),也需要200萬(wàn)個(gè)線程,顯然這是不可行的。
工作竊取算法
forkjoin最核心的地方就是利用了現(xiàn)代硬件設(shè)備多核,在一個(gè)操作時(shí)候會(huì)有空閑的cpu,那么如何利用好這個(gè)空閑的cpu就成了提高性能的關(guān)鍵,而這里我們要提到的工作竊?。╳ork-stealing)算法就是整個(gè)forkjion框架的核心理念,工作竊?。╳ork-stealing)算法是指某個(gè)線程從其他隊(duì)列里竊取任務(wù)來(lái)執(zhí)行。
那么為什么需要使用工作竊取算法呢?
假如我們需要做一個(gè)比較大的任務(wù),我們可以把這個(gè)任務(wù)分割為若干互不依賴的子任務(wù),為了減少線程間的競(jìng)爭(zhēng),于是把這些子任務(wù)分別放到不同的隊(duì)列里,并為每個(gè)隊(duì)列創(chuàng)建一個(gè)單獨(dú)的線程來(lái)執(zhí)行隊(duì)列里的任務(wù),線程和隊(duì)列一一對(duì)應(yīng),比如A線程負(fù)責(zé)處理A隊(duì)列里的任務(wù)。但是有的線程會(huì)先把自己隊(duì)列里的任務(wù)干完,而其他線程對(duì)應(yīng)的隊(duì)列里還有任務(wù)等待處理。干完活的線程與其等著,不如去幫其他線程干活,于是它就去其他線程的隊(duì)列里竊取一個(gè)任務(wù)來(lái)執(zhí)行。而在這時(shí)它們會(huì)訪問(wèn)同一個(gè)隊(duì)列,所以為了減少竊取任務(wù)線程和被竊取任務(wù)線程之間的競(jìng)爭(zhēng),通常會(huì)使用雙端隊(duì)列,被竊取任務(wù)線程永遠(yuǎn)從雙端隊(duì)列的頭部拿任務(wù)執(zhí)行,而竊取任務(wù)的線程永遠(yuǎn)從雙端隊(duì)列的尾部拿任務(wù)執(zhí)行。
工作竊取算法的優(yōu)點(diǎn)是充分利用線程進(jìn)行并行計(jì)算,并減少了線程間的競(jìng)爭(zhēng),其缺點(diǎn)是在某些情況下還是存在競(jìng)爭(zhēng),比如雙端隊(duì)列里只有一個(gè)任務(wù)時(shí)。并且消耗了更多的系統(tǒng)資源,比如創(chuàng)建多個(gè)線程和多個(gè)雙端隊(duì)列。
用看forkjion的眼光來(lái)看ParallelStreams
上文中已經(jīng)提到了在Java 8引入了自動(dòng)并行化的概念。它能夠讓一部分Java代碼自動(dòng)地以并行的方式執(zhí)行,也就是我們使用了ForkJoinPool的ParallelStream。
Java 8為ForkJoinPool添加了一個(gè)通用線程池,這個(gè)線程池用來(lái)處理那些沒(méi)有被顯式提交到任何線程池的任務(wù)。它是ForkJoinPool類型上的一個(gè)靜態(tài)元素,它擁有的默認(rèn)線程數(shù)量等于運(yùn)行計(jì)算機(jī)上的處理器數(shù)量。當(dāng)調(diào)用Arrays類上添加的新方法時(shí),自動(dòng)并行化就會(huì)發(fā)生。比如用來(lái)排序一個(gè)數(shù)組的并行快速排序,用來(lái)對(duì)一個(gè)數(shù)組中的元素進(jìn)行并行遍歷。自動(dòng)并行化也被運(yùn)用在Java 8新添加的Stream API中。
比如下面的代碼用來(lái)遍歷列表中的元素并執(zhí)行需要的操作:
List<UserInfo> userInfoList = DaoContainers.getUserInfoDAO().queryAllByList(new UserInfoModel()); userInfoList.parallelStream().forEach(redisUserApi::setUserIdUserInfo);
對(duì)于列表中的元素的操作都會(huì)以并行的方式執(zhí)行。forEach方法會(huì)為每個(gè)元素的計(jì)算操作創(chuàng)建一個(gè)任務(wù),該任務(wù)會(huì)被前文中提到的ForkJoinPool中的通用線程池處理。以上的并行計(jì)算邏輯當(dāng)然也可以使用ThreadPoolExecutor完成,但是就代碼的可讀性和代碼量而言,使用ForkJoinPool明顯更勝一籌。
對(duì)于ForkJoinPool通用線程池的線程數(shù)量,通常使用默認(rèn)值就可以了,即運(yùn)行時(shí)計(jì)算機(jī)的處理器數(shù)量。我這里提供了一個(gè)示例的代碼讓你了解jvm所使用的ForkJoinPool的線程數(shù)量, 你可以可以通過(guò)設(shè)置系統(tǒng)屬性:-Djava.util.concurrent.ForkJoinPool.common.parallelism=N (N為線程數(shù)量),來(lái)調(diào)整ForkJoinPool的線程數(shù)量,可以嘗試調(diào)整成不同的參數(shù)來(lái)觀察每次的輸出結(jié)果:
import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CountDownLatch; /** * @description 這是一個(gè)用來(lái)讓你更加熟悉parallelStream的原理的實(shí)力 * @date 2016年10月11日18:26:55 * @version v1.0 * @author wangguangdong */ public class App { public static void main(String[] args) throws Exception { System.out.println("Hello World!"); // 構(gòu)造一個(gè)10000個(gè)元素的集合 List<Integer> list = new ArrayList<>(); for (int i = 0; i < 10000; i++) { list.add(i); } // 統(tǒng)計(jì)并行執(zhí)行l(wèi)ist的線程 Set<Thread> threadSet = new CopyOnWriteArraySet<>(); // 并行執(zhí)行 list.parallelStream().forEach(integer -> { Thread thread = Thread.currentThread(); // System.out.println(thread); // 統(tǒng)計(jì)并行執(zhí)行l(wèi)ist的線程 threadSet.add(thread); }); System.out.println("threadSet一共有" + threadSet.size() + "個(gè)線程"); System.out.println("系統(tǒng)一個(gè)有"+Runtime.getRuntime().availableProcessors()+"個(gè)cpu"); List<Integer> list1 = new ArrayList<>(); List<Integer> list2 = new ArrayList<>(); for (int i = 0; i < 100000; i++) { list1.add(i); list2.add(i); } Set<Thread> threadSetTwo = new CopyOnWriteArraySet<>(); CountDownLatch countDownLatch = new CountDownLatch(2); Thread threadA = new Thread(() -> { list1.parallelStream().forEach(integer -> { Thread thread = Thread.currentThread(); // System.out.println("list1" + thread); threadSetTwo.add(thread); }); countDownLatch.countDown(); }); Thread threadB = new Thread(() -> { list2.parallelStream().forEach(integer -> { Thread thread = Thread.currentThread(); // System.out.println("list2" + thread); threadSetTwo.add(thread); }); countDownLatch.countDown(); }); threadA.start(); threadB.start(); countDownLatch.await(); System.out.print("threadSetTwo一共有" + threadSetTwo.size() + "個(gè)線程"); System.out.println("---------------------------"); System.out.println(threadSet); System.out.println(threadSetTwo); System.out.println("---------------------------"); threadSetTwo.addAll(threadSet); System.out.println(threadSetTwo); System.out.println("threadSetTwo一共有" + threadSetTwo.size() + "個(gè)線程"); System.out.println("系統(tǒng)一個(gè)有"+Runtime.getRuntime().availableProcessors()+"個(gè)cpu"); } }
出現(xiàn)這種現(xiàn)象的原因是,forEach方法用了一些小把戲。它會(huì)將執(zhí)行forEach本身的線程也作為線程池中的一個(gè)工作線程。因此,即使將ForkJoinPool的通用線程池的線程數(shù)量設(shè)置為1,實(shí)際上也會(huì)有2個(gè)工作線程。因此在使用forEach的時(shí)候,線程數(shù)為1的ForkJoinPool通用線程池和線程數(shù)為2的ThreadPoolExecutor是等價(jià)的。
所以當(dāng)ForkJoinPool通用線程池實(shí)際需要4個(gè)工作線程時(shí),可以將它設(shè)置成3,那么在運(yùn)行時(shí)可用的工作線程就是4了。
小結(jié):
1. 當(dāng)需要處理遞歸分治算法時(shí),考慮使用ForkJoinPool。
2. 仔細(xì)設(shè)置不再進(jìn)行任務(wù)劃分的閾值,這個(gè)閾值對(duì)性能有影響。
3. Java 8中的一些特性會(huì)使用到ForkJoinPool中的通用線程池。在某些場(chǎng)合下,需要調(diào)整該線程池的默認(rèn)的線程數(shù)量。
ParallelStreams 的陷阱
上文中我們已經(jīng)看到了ParallelStream他強(qiáng)大無(wú)比的特性,但這里我們就講告訴你ParallelStreams不是萬(wàn)金油,而是一把雙刃劍,如果錯(cuò)誤的使用反倒可能傷人傷己.
以下是一個(gè)我們項(xiàng)目里使用 parallel streams 的很常見(jiàn)的情況。在這個(gè)例子中,我們想同時(shí)調(diào)用不同地址的api中并且獲得第一個(gè)返回的結(jié)果。
public static String query(String q, List<String> engines) { Optional<String> result = engines.stream().parallel().map((base) -> { String url = base + q; return WS.url(url).get(); }).findAny(); return result.get(); }
可能有很多朋友在jdk7用future配合countDownLatch自己實(shí)現(xiàn)的這個(gè)功能,但是jdk8的朋友基本都會(huì)用上面的實(shí)現(xiàn)方式,那么自信深究一下究竟自己用future實(shí)現(xiàn)的這個(gè)功能和利用jdk8的parallelStream來(lái)實(shí)現(xiàn)這個(gè)功能有什么不同點(diǎn)呢?坑又在哪里呢?
讓我們細(xì)思思考一下整個(gè)功能究竟是如何運(yùn)轉(zhuǎn)的。首先我們的集合元素engines 由ParallelStreams并行的去進(jìn)行map操作(ParallelStreams使用JVM默認(rèn)的forkJoin框架的線程池由當(dāng)前線程去執(zhí)行并行操作).
然而,這里需要注意的一地方是我們?cè)谡{(diào)用第三方的api請(qǐng)求是一個(gè)響應(yīng)略慢而且會(huì)阻塞操作的一個(gè)過(guò)程。所以在某時(shí)刻所有線程都會(huì)調(diào)用 get() 方法并且在那里等待結(jié)果返回.
再回過(guò)頭仔細(xì)思考一下這個(gè)功能的實(shí)現(xiàn)過(guò)程是我們一開(kāi)始想要的嗎?我們是在同一時(shí)間等待所有的結(jié)果,而不是遍歷這個(gè)列表按順序等待每個(gè)回答.然而,由于ForkJoinPool workders的存在,這樣平行的等待相對(duì)于使用主線程的等待會(huì)產(chǎn)生的一種副作用.
現(xiàn)在ForkJoin pool (關(guān)于forkjion的更多實(shí)現(xiàn)你可以去搜索引擎中去看一下他的具體實(shí)現(xiàn)方式) 的實(shí)現(xiàn)是: 它并不會(huì)因?yàn)楫a(chǎn)生了新的workers而抵消掉阻塞的workers。那么在某個(gè)時(shí)間所有 ForkJoinPool.common() 的線程都會(huì)被用光.也就是說(shuō),下一次你調(diào)用這個(gè)查詢方法,就可能會(huì)在一個(gè)時(shí)間與其他的parallel stream同時(shí)運(yùn)行,而導(dǎo)致第二個(gè)任務(wù)的性能大大受損?;蛘哒f(shuō),例如你在這個(gè)功能里是用來(lái)快速返回調(diào)用的第三方api的,而在其他的功能里是用于一些簡(jiǎn)單的數(shù)據(jù)并行計(jì)算的,但是假如你先調(diào)用了這個(gè)功能,同一時(shí)間之后調(diào)用計(jì)算的函數(shù),那么這里forkjionPool的實(shí)現(xiàn)會(huì)讓你計(jì)算的函數(shù)大打折扣.
不過(guò)也不要急著去吐槽ForkJoinPool的實(shí)現(xiàn),在不同的情況下你可以給它一個(gè)ManagedBlocker實(shí)例并且確保它知道在一個(gè)阻塞調(diào)用中應(yīng)該什么時(shí)候去抵消掉卡住的workers.現(xiàn)在有意思的一點(diǎn)是,在一個(gè)parallel stream處理中并不一定是阻塞調(diào)用會(huì)拖延程序的性能。任何被用于映射在一個(gè)集合上的長(zhǎng)時(shí)間運(yùn)行的函數(shù)都會(huì)產(chǎn)生同樣的問(wèn)題.
正如我們上面那個(gè)列子的情況分析得知,lambda的執(zhí)行并不是瞬間完成的,所有使用parallel streams的程序都有可能成為阻塞程序的源頭,并且在執(zhí)行過(guò)程中程序中的其他部分將無(wú)法訪問(wèn)這些workers,這意味著任何依賴parallel streams的程序在什么別的東西占用著common ForkJoinPool時(shí)將會(huì)變得不可預(yù)知并且暗藏危機(jī).
怎么正確使用parallelStream
如果你正在寫(xiě)一個(gè)其他地方都是單線程的程序并且準(zhǔn)確地知道什么時(shí)候你應(yīng)該要使用parallel streams,這樣的話你可能會(huì)覺(jué)得這個(gè)問(wèn)題有一點(diǎn)膚淺。然而,我們很多人是在處理web應(yīng)用、各種不同的框架以及重量級(jí)應(yīng)用服務(wù)。一個(gè)服務(wù)器是怎樣被設(shè)計(jì)成一個(gè)可以支持多種獨(dú)立應(yīng)用的主機(jī)的?誰(shuí)知道呢,給你一個(gè)可以并行的卻不能控制輸入的parallel stream.
很抱歉,請(qǐng)?jiān)徫矣玫臉?biāo)注[怎么正確使用parallelStream],因?yàn)槟壳盀橹刮乙矝](méi)有發(fā)現(xiàn)一個(gè)好的方式來(lái)讓我真正的正確使用parallelStream.下面的網(wǎng)上寫(xiě)的兩種方式:
一種方式是限制ForkJoinPool提供的并行數(shù)??梢酝ㄟ^(guò)使用-Djava.util.concurrent.ForkJoinPool.common.parallelism=1 來(lái)限制線程池的大小為1。不再?gòu)牟⑿谢械玫胶锰幙梢远沤^錯(cuò)誤的使用它(其實(shí)這個(gè)方式還是有點(diǎn)搞笑的,既然這樣搞那我還不如不去使用并行流)。
另一種方式就是,一個(gè)被稱為工作區(qū)的可以讓ForkJoinPool平行放置的 parallelStream() 實(shí)現(xiàn)。不幸的是現(xiàn)在的JDK還沒(méi)有實(shí)現(xiàn)。
Parallel streams 是無(wú)法預(yù)測(cè)的,而且想要正確地使用它有些棘手。幾乎任何parallel streams的使用都會(huì)影響程序中無(wú)關(guān)部分的性能,而且是一種無(wú)法預(yù)測(cè)的方式。。但是在調(diào)用stream.parallel() 或者parallelStream()時(shí)候在我的代碼里之前我仍然會(huì)重新審視一遍他給我的程序究竟會(huì)帶來(lái)什么問(wèn)題,他能有多大的提升,是否有使用他的意義.
stream or parallelStream?
上面我們也看到了parallelStream所帶來(lái)的隱患和好處,那么,在從stream和parallelStream方法中進(jìn)行選擇時(shí),我們可以考慮以下幾個(gè)問(wèn)題:
1. 是否需要并行?
2. 任務(wù)之間是否是獨(dú)立的?是否會(huì)引起任何競(jìng)態(tài)條件?
3. 結(jié)果是否取決于任務(wù)的調(diào)用順序?
對(duì)于問(wèn)題1,在回答這個(gè)問(wèn)題之前,你需要弄清楚你要解決的問(wèn)題是什么,數(shù)據(jù)量有多大,計(jì)算的特點(diǎn)是什么?并不是所有的問(wèn)題都適合使用并發(fā)程序來(lái)求解,比如當(dāng)數(shù)據(jù)量不大時(shí),順序執(zhí)行往往比并行執(zhí)行更快。畢竟,準(zhǔn)備線程池和其它相關(guān)資源也是需要時(shí)間的。但是,當(dāng)任務(wù)涉及到I/O操作并且任務(wù)之間不互相依賴時(shí),那么并行化就是一個(gè)不錯(cuò)的選擇。通常而言,將這類程序并行化之后,執(zhí)行速度會(huì)提升好幾個(gè)等級(jí)。
對(duì)于問(wèn)題2,如果任務(wù)之間是獨(dú)立的,并且代碼中不涉及到對(duì)同一個(gè)對(duì)象的某個(gè)狀態(tài)或者某個(gè)變量的更新操作,那么就表明代碼是可以被并行化的。
對(duì)于問(wèn)題3,由于在并行環(huán)境中任務(wù)的執(zhí)行順序是不確定的,因此對(duì)于依賴于順序的任務(wù)而言,并行化也許不能給出正確的結(jié)果。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
當(dāng)前名稱:通過(guò)實(shí)例解析java8中的parallelStream
URL地址:http://aaarwkj.com/article8/goohip.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、外貿(mào)建站、企業(yè)建站、品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站導(dǎo)航、網(wǎng)站排名
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
移動(dòng)網(wǎng)站建設(shè)知識(shí)