日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
并行流?再用打斷狗腿!

本文轉(zhuǎn)載自微信公眾號(hào)「小姐姐味道」,作者小姐姐養(yǎng)的狗。轉(zhuǎn)載本文請(qǐng)聯(lián)系小姐姐味道公眾號(hào)。

成都創(chuàng)新互聯(lián)公司是一家專業(yè)提供雨城企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計(jì)制作、做網(wǎng)站、成都h5網(wǎng)站建設(shè)、小程序制作等業(yè)務(wù)。10年已為雨城眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡(luò)公司優(yōu)惠進(jìn)行中。

很久之前,xjjdog就有一篇文章,詳細(xì)分析了為什么不要隨便使用并行流,因?yàn)槔锩婵佣嗳馍?,還隱藏了很多不為人知的超級(jí)惡心的小秘密。

parallelStream的坑,不踩不知道,一踩嚇一跳

但今天還是在線上的故障中,又一次碰見了它。相對(duì)于parallelStream可能讓程序運(yùn)行的更緩慢(沒錯(cuò)),更要命的是它會(huì)讓你的程序拋出異常,運(yùn)行變得不準(zhǔn)確。當(dāng)最終確認(rèn)了根本的問題,一股惡心的感覺涌上心頭。我干嘔了幾聲,心情難以言表。

這個(gè)場景在我們上篇文章中,被判定是小兒科。但即使是這么小兒科的代碼,還是有人中招,還是要對(duì)并發(fā)編程有一點(diǎn)敬畏之心呀,不是很懂的api弄懂才能用。

問題原因

先來看看這段小代碼吧。

  
 
 
  1. List transform(List source){ 
  2.  List dst = new ArrayList<>(); 
  3.  if(CollectionUtils.isEmpty()){ 
  4.   return dst; 
  5.  } 
  6.  source.stream. 
  7.   .parallel() 
  8.   .map(..) 
  9.   .filter(..) 
  10.   .foreach(dst::add); 
  11.  return dst; 

程序很簡單,期望使用stream的方式,把一個(gè)list經(jīng)過轉(zhuǎn)化和過濾之后,轉(zhuǎn)化為另外一個(gè)list。尤其注意的是,代碼使用了parallel(),意思是底層會(huì)通過forkjoin的方式,去運(yùn)行你的代碼。

上線之后,應(yīng)用發(fā)生了詭異的反應(yīng)。在返回的List中,某些數(shù)據(jù)有時(shí)候出現(xiàn),有時(shí)候又消失不見,就像是被阿里公關(guān)下的熱搜一樣,成為了幽靈數(shù)據(jù)。更有趣的是,它還會(huì)拋異常。

追根究底

其實(shí),明眼人一看parallell這個(gè)關(guān)鍵字,就恨得牙癢癢。在并行的方法里使用線程不安全的集合類,是Java編程之大忌。

讓我們強(qiáng)行去掉這些干擾因素,來模擬這個(gè)數(shù)據(jù)丟失情況。

  
 
 
  1. public class xx { 
  2.     static List transform(List source){ 
  3.         List dst = new ArrayList<>(); 
  4.         source.stream().parallel().map(i -> i*10).forEach(dst::add); 
  5.         return dst; 
  6.     } 
  7.     public static void main(String[] args) { 
  8.         List source = new ArrayList<>(); 
  9.         for(int i=0;i<500;i++){ 
  10.             source.add(i); 
  11.         } 
  12.         for(int i=0;i<100;i++) { 
  13.             System.out.println("size = " + transform(source).size()); 
  14.         } 
  15.     } 

我們主要是對(duì)500條數(shù)據(jù)進(jìn)行轉(zhuǎn)換。很快,你將會(huì)看到異常。但大多數(shù)情況下,數(shù)據(jù)條數(shù)根本達(dá)不到500條,部分?jǐn)?shù)據(jù),離奇的消失了。

  
 
 
  1. size = 499 
  2. size = 500 
  3. size = 484 
  4. size = 500 
  5. Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException 
  6.  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
  7.  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) 
  8.  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
  9.  at java.lang.reflect.Constructor.newInstance(Constructor.java:423) 
  10.  at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:598) 
  11.  at java.util.concurrent.ForkJoinTask.reportException(ForkJoinTask.java:677) 
  12.  at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:735) 

既然是并行,那用屁股想一想,就知道這里面肯定會(huì)有線程安全問題。不過我們這里討論的并不是要你使用線程安全的集合,這個(gè)話題太低級(jí)。現(xiàn)階段,知道在線程不安全的環(huán)境中使用線程安全的集合,已經(jīng)是一個(gè)基本的技能。

我現(xiàn)在收回上面的話,因?yàn)槲野l(fā)現(xiàn)它并不是一個(gè)基本的技能。

對(duì)于ArrayList來說,它的add操作,并不是線程安全的,并不是一個(gè)原子操作。

  
 
 
  1. public boolean add(E e) { 
  2.   ensureCapacityInternal(size + 1);  
  3.   elementData[size++] = e; 
  4.   return true; 

你看上面的代碼多明顯啊,先需要讀取size的值,然后有一個(gè)加1操作,然后又有一個(gè)自增操作。這種代碼,說什么也是不敢用在多線程環(huán)境下的。

總結(jié)

相同的道理,你也不是這樣去搞普通的map,普通的queue,那都不是安全的操作。

還是建議你讀一下它更隱秘的坑。

parallelStream的坑,不踩不知道,一踩嚇一跳

實(shí)際上,我是非常的不建議你在任何時(shí)候,使用parallelStream或者parallel函數(shù)。一旦你在代碼里發(fā)現(xiàn)了它,請(qǐng)干掉它,并向它吐一口唾沫,就當(dāng)它從未在jdk中存在過。相對(duì)于它增加的那納兒毫秒的速度,它所引入的問題才是更加要命的。

事實(shí)上,我已經(jīng)在sonar的檢測規(guī)則中加入了它,讓它徹底在我的視野中消失。

作者簡介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。


新聞名稱:并行流?再用打斷狗腿!
URL分享:http://www.dlmjj.cn/article/djscpgg.html