自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

安全 應(yīng)用安全
很多同學(xué)喜歡使用lambda表達(dá)式,它允許你定義短小精悍的函數(shù),體現(xiàn)你高超的編碼水平。當(dāng)然,這個(gè)功能在某些以代碼行數(shù)來(lái)衡量工作量的公司來(lái)說(shuō),就比較吃虧一些。

[[342087]]

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

很多同學(xué)喜歡使用lambda表達(dá)式,它允許你定義短小精悍的函數(shù),體現(xiàn)你高超的編碼水平。當(dāng)然,這個(gè)功能在某些以代碼行數(shù)來(lái)衡量工作量的公司來(lái)說(shuō),就比較吃虧一些。

比如下面的代碼片段,讓人閱讀的時(shí)候就像是讀詩(shī)一樣。但是一旦用不好,也是會(huì)要命的。

  1. List<Integer> transactionsIds = 
  2. widgets.stream() 
  3.              .filter(b -> b.getColor() == RED) 
  4.              .sorted((x,y) -> x.getWeight() - y.getWeight()) 
  5.              .mapToInt(Widget::getWeight) 
  6.              .sum(); 

這段代碼有一個(gè)關(guān)鍵的函數(shù),那就是stream。通過(guò)它,可以將一個(gè)普通的list,轉(zhuǎn)化為流,然后就可以使用類似于管道的方式對(duì)list進(jìn)行操作??傊眠^(guò)的都說(shuō)好。

對(duì)這些函數(shù)還不是太熟悉?可以參考:《到處是map、flatMap,啥意思?》

問(wèn)題來(lái)了

假如我們把stream換成parallelStream,會(huì)發(fā)生什么情況?

根據(jù)字面上的意思,流會(huì)從串行 變成并行。

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

這次踩坑的地方,是并行流的性能問(wèn)題。

我們用代碼來(lái)說(shuō)話。

下面的代碼,開(kāi)啟了8個(gè)線程,這8個(gè)線程都在使用并行流進(jìn)行數(shù)據(jù)計(jì)算。在執(zhí)行的邏輯中,我們讓每個(gè)任務(wù)都sleep 1秒鐘,這樣就能夠模擬一些I/O請(qǐng)求的耗時(shí)等待。

使用stream,程序會(huì)在30秒后返回,但我們期望程序能夠在1秒多返回,因?yàn)樗遣⑿辛鳎脤?duì)得起這個(gè)稱號(hào)。

測(cè)試發(fā)現(xiàn),我們等了好久,任務(wù)才執(zhí)行完畢。

  1. static void paralleTest() { 
  2.     List<Integer> numbers = Arrays.asList( 
  3.             0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 
  4.             10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 
  5.             20, 21, 22, 23, 24, 25, 26, 27, 28, 29 
  6.     ); 
  7.     final long begin = System.currentTimeMillis(); 
  8.     numbers.parallelStream().map(k -> { 
  9.         try { 
  10.             Thread.sleep(1000); 
  11.             System.out.println((System.currentTimeMillis() - begin) + "ms => " + k + " \t" + Thread.currentThread()); 
  12.         } catch (InterruptedException e) { 
  13.             e.printStackTrace(); 
  14.         } 
  15.         return k; 
  16.     }).collect(Collectors.toList()); 
  17.  
  18. public static void main(String[] args) { 
  19. //    System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism""20"); 
  20.     new Thread(() -> paralleTest()).start(); 
  21.     new Thread(() -> paralleTest()).start(); 
  22.     new Thread(() -> paralleTest()).start(); 
  23.     new Thread(() -> paralleTest()).start(); 
  24.     new Thread(() -> paralleTest()).start(); 
  25.     new Thread(() -> paralleTest()).start(); 
  26.     new Thread(() -> paralleTest()).start(); 
  27.     new Thread(() -> paralleTest()).start(); 

實(shí)際上,在不同的機(jī)器上執(zhí)行,這段代碼花費(fèi)的時(shí)間都不一樣。

既然是并行,那肯定得有個(gè)并行度。太低了,體現(xiàn)不到并行的能能力;太大了,又浪費(fèi)了上下文切換的時(shí)間。我是很沮喪的發(fā)現(xiàn),很多高級(jí)研發(fā),將線程池的各種參數(shù)背的滾瓜爛熟,各種調(diào)優(yōu),竟然敢睜一只眼閉一只眼的在I/O密集型業(yè)務(wù)中用上parallelStream。

要了解這個(gè)并行度,我們需要查看具體的構(gòu)造方法。在ForkJoinPool類中找到這樣的代碼。

  1. try {  // ignore exceptions in accessing/parsing properties 
  2.     String pp = System.getProperty 
  3.         ("java.util.concurrent.ForkJoinPool.common.parallelism"); 
  4.     if (pp != null
  5.         parallelism = Integer.parseInt(pp); 
  6.     fac = (ForkJoinWorkerThreadFactory) newInstanceFromSystemProperty( 
  7.         "java.util.concurrent.ForkJoinPool.common.threadFactory"); 
  8.     handler = (UncaughtExceptionHandler) newInstanceFromSystemProperty( 
  9.         "java.util.concurrent.ForkJoinPool.common.exceptionHandler"); 
  10. } catch (Exception ignore) { 
  11.  
  12. if (fac == null) { 
  13.     if (System.getSecurityManager() == null
  14.         fac = defaultForkJoinWorkerThreadFactory; 
  15.     else // use security-managed default 
  16.         fac = new InnocuousForkJoinWorkerThreadFactory(); 
  17. if (parallelism < 0 && // default 1 less than #cores 
  18.     (parallelism = Runtime.getRuntime().availableProcessors() - 1) <= 0) 
  19.     parallelism = 1; 
  20. if (parallelism > MAX_CAP) 
  21.     parallelism = MAX_CAP; 

可以看到,并行度到底是多少,是由下面的參數(shù)來(lái)控制的。如果無(wú)法獲取這個(gè)參數(shù),則默認(rèn)使用 CPU個(gè)數(shù)-1 的并行度。

可以看到,這個(gè)函數(shù)是為了計(jì)算密集型業(yè)務(wù)去設(shè)計(jì)的。如果你喂給它一大堆任務(wù),它就會(huì)由并行執(zhí)行退變成類似于串行的效果。

  1. -Djava.util.concurrent.ForkJoinPool.common.parallelism=N 

即使你使用-Djava.util.concurrent.ForkJoinPool.common.parallelism=N設(shè)置了一個(gè)初始值大小,它依然有問(wèn)題。

因?yàn)?,parallelism這個(gè)變量是final的,一旦設(shè)定,不允許修改。也就是說(shuō),上面的參數(shù)只會(huì)生效一次。

張三可能使用下面的代碼,設(shè)置了并行度大小為20。

  1. System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism""20"); 

李四可能用同樣的方式,設(shè)置了這個(gè)值為30。那實(shí)際在項(xiàng)目中用的是哪個(gè)值,那就得問(wèn)JVM是怎么加載的類信息了。

這種方式并不太非??孔V。

一種解決方式

我們可以通過(guò)提供外置的forkjoinpool,也就是改變提交方式,來(lái)實(shí)現(xiàn)不同類型的任務(wù)分離。

代碼如下所示,通過(guò)顯式的代碼提交,即可實(shí)現(xiàn)任務(wù)分離。

  1. ForkJoinPool pool = new ForkJoinPool(30); 
  2.  
  3. final long begin = System.currentTimeMillis(); 
  4. try { 
  5.     pool.submit(() -> 
  6.             numbers.parallelStream().map(k -> { 
  7.                 try { 
  8.                     Thread.sleep(1000); 
  9.                     System.out.println((System.currentTimeMillis() - begin) + "ms => " + k + " \t" + Thread.currentThread()); 
  10.                 } catch (InterruptedException e) { 
  11.                     e.printStackTrace(); 
  12.                 } 
  13.                 return k; 
  14.             }).collect(Collectors.toList())).get(); 
  15. } catch (InterruptedException e) { 
  16.     e.printStackTrace(); 
  17. } catch (ExecutionException e) { 
  18.     e.printStackTrace(); 

這樣,不同的場(chǎng)景,就可以擁有不同的并行度。這種方式和CountDownLatch有異曲同工之妙,我們需要手動(dòng)管理資源。

使用了這種方式,代碼量增加,已經(jīng)和優(yōu)雅關(guān)系不大了,不僅不優(yōu)雅,而且丑的要命。白天鵝變成了丑小鴨,你還會(huì)愛(ài)它么?

 

作者簡(jiǎn)介:小姐姐味道 (xjjdog),一個(gè)不允許程序員走彎路的公眾號(hào)。聚焦基礎(chǔ)架構(gòu)和Linux。十年架構(gòu),日百億流量,與你探討高并發(fā)世界,給你不一樣的味道。我的個(gè)人微信xjjdog0,歡迎添加好友,進(jìn)一步交流。

 

責(zé)任編輯:武曉燕 來(lái)源: 小姐姐味道
相關(guān)推薦

2018-05-07 15:44:44

工資騰訊阿里

2023-02-15 17:32:15

2009-06-01 08:45:25

iPhone蘋(píng)果移動(dòng)OS

2015-05-22 14:06:16

百度百度搜索這些詞

2022-01-07 11:48:59

RabbitMQGolang 項(xiàng)目

2019-04-18 14:06:35

MySQL分庫(kù)分表數(shù)據(jù)庫(kù)

2024-04-01 08:05:27

Go開(kāi)發(fā)Java

2023-02-20 08:11:04

2009-08-21 10:56:00

2023-01-18 23:20:25

編程開(kāi)發(fā)

2020-09-15 08:46:26

Kubernetes探針服務(wù)端

2018-03-07 15:19:07

2021-07-28 05:01:29

Lombok前端測(cè)試

2022-07-15 13:09:33

Three.js前端

2024-02-22 08:37:28

NodejsJavaScript運(yùn)行

2017-07-17 15:46:20

Oracle并行機(jī)制

2025-04-29 10:17:42

2024-01-09 07:39:20

maven特性版本

2017-05-05 08:12:51

Spark共享變量
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)