您好,登錄后才能下訂單哦!
這篇文章主要介紹了如何解決JDK8的ParallelStream遍歷無序的問題,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
它通過默認的ForkJoinPool,可能提高你的多線程任務(wù)的速度.
Stream具有平行處理能力,處理的過程會分而治之,也就是將一個大任務(wù)切分成多個小任務(wù),這表示每個任務(wù)都是一個操作,因此像以下的程式片段:
List list = Arrays.asList(1, 2, 3, 4, 5); list.parallelStream().forEach(out::println);
你得到的展示順序不一定會是1、2、3、4、5,而可能是任意的順序,就forEach()這個操作來講,如果平行處理時,希望最后順序是按照原來Stream的數(shù)據(jù)順序,那可以調(diào)用forEachOrdered()。
List list = Arrays.asList(1, 2, 3, 4, 5); list.parallelStream().forEachOrdered(out::println);
Java8 已經(jīng)很久了,現(xiàn)在都已經(jīng)Java12版本了.
我所在的上家公司,在寫代碼時候推薦使用lambad來進行操作遍歷集合
也就是像下面一樣
List<Integer> list = new ArrayList<>(); for (int j = 0; j < 1000; j++) { list.add(j); } list.stream().forEach(value -> { System.out.println(value); });
這種效率其實和傳統(tǒng)上的使用foreach以及for循環(huán)遍歷效果差不多,因為點開forEach方法會發(fā)現(xiàn)內(nèi)部其實使用的是下面的方法進行對集合遍歷的
內(nèi)部其實使用的還是for進行遍歷,所以兩者相比較其實沒有什么效率的差異的,當(dāng)然這也會由于每個公司編程習(xí)慣不一樣,有的人更喜歡傳統(tǒng)上的for進行遍歷
因為上面的遍歷方式不會對效率有什么提升, 所以由此還有一種方式就是
parallelStream()
List<Integer> list = new ArrayList<>(); for (int j = 0; j < 1000; j++) { list.add(j); } list.parallelStream().forEach(value -> { System.out.println(value); });
上面的方法其實就是異步的,
這種遍歷方式因為是異步遍歷,會產(chǎn)生一種情況,就是遍歷的順序是無序的,當(dāng)然也有相應(yīng)的好處就是,遍歷速度會快,當(dāng)對生成結(jié)果不考慮排序問題而且數(shù)據(jù)量比較大的時候可以使用.
但是,有利自然有弊,因為異步的所以需要考慮線程的問題,就是生成的結(jié)果真的是你想要的么?
以下面的例子來運行一段代碼:
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); for (int j = 0; j < 1000; j++) { list.add(j); } System.out.println("最開始生成的集合長度:"+list.size()); //parallelStream遍歷數(shù)據(jù)的時候會產(chǎn)生丟失的問題 for (int i = 0; i < 10 ; i++) { List<Integer> parseList = new ArrayList<>(); list.parallelStream().forEach(integer -> { parseList.add(integer); }); System.out.println("每次遍歷的集合長度:"+ parseList.size()); } }
我首先創(chuàng)建了一個1000長度的集合,之后對這個集合使用多次遍歷,然而呢,會發(fā)現(xiàn),最后遍歷的集合少數(shù)據(jù),并且會在多次重復(fù)遍歷的時候數(shù)組越界..
因為這種情況,之前工作使用parallelStream出現(xiàn)過2次問題, 我一直以為是使用parallelStream本身不夠很安全導(dǎo)致的.實際上今天整理這篇博文突然才發(fā)現(xiàn)這個問題,就是遍歷的結(jié)果轉(zhuǎn)為的list是線程安全的么?
其實當(dāng)正常進行遍歷的時候, 可以對遍歷出的結(jié)果核對,實際上每次遍歷出的結(jié)果,仍然是與原來生成的結(jié)果一致的.
所以這邊只能將鍋甩在接收這些數(shù)據(jù)的list上面了
這個時候就需要對list進行包裝
List<Integer> synchronizedList = Collections.synchronizedList(parseList);
這會在看下修改后的代碼以及結(jié)果
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); for (int j = 0; j < 1000; j++) { list.add(j); } System.out.println("最開始生成的集合長度:"+list.size()); //parallelStream遍歷數(shù)據(jù)的時候會產(chǎn)生丟失的問題 for (int i = 0; i < 10 ; i++) { List<Integer> parseList = new ArrayList<>(); List<Integer> synchronizedList = Collections.synchronizedList(parseList); list.parallelStream().forEach(integer -> { synchronizedList.add(integer); }); System.out.println("每次遍歷的集合長度:"+ synchronizedList.size()); } }
這樣每次遍歷的結(jié)果也都是一樣的,而且速度也會由于異步的會比之前效率提升好多
同樣的如何創(chuàng)建線程安全的set,map也就可以進行相應(yīng)的包裝,這樣就避免了使用會出新一些明明感覺對,確和自己想要的結(jié)果不一致的bug
同理使用parallelStream用StringBuffer 而不適用StringBuilder,因為前者是線程安全的
感謝你能夠認真閱讀完這篇文章,希望小編分享的“如何解決JDK8的ParallelStream遍歷無序的問題”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。