2017-05-08 33 views
4

我想了解java 8並行流。我寫了下面的代碼,首先使用Executor,然後使用並行流。 似乎並行流正在執行兩次(10秒)的時間與Executor方法(5秒)一樣多。在我看來,平行流也應該表現出類似的表現。任何想法爲什麼並行流需要兩倍時間? 我的電腦有8個內核。java 8並行流需要更多時間

/** 
* 
*/ 
package com.shashank.java8.parallel_stream; 

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Date; 
import java.util.List; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Future; 

/** 
* @author pooja 
* 
*/ 
public class Sample { 

    public static int processUrl(String url) { 

     try { 
      Thread.sleep(5000); 
     } catch (InterruptedException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
     System.out.println("Running Thread " + Thread.currentThread()); 
     return url.length(); 
    } 

    /** 
    * @param args 
    * @throws Exception 
    */ 
    public static void main(String[] args) throws Exception { 
     usingExecutor(); 
     usingParallelStream(); 
    } 

    public static void usingParallelStream() { 

     Date start = new Date(); 
     // TODO Auto-generated method stub 
     int total = buildUrlsList().parallelStream().mapToInt(Sample::processUrl).reduce(0, Integer::sum); 
     Date end = new Date(); 
     System.out.println(total); 
     System.out.println((end.getTime() - start.getTime())/1000); 

    } 

    public static void usingExecutor() throws Exception { 
     Date start = new Date(); 
     ExecutorService executorService = Executors.newFixedThreadPool(100); 
     List<Future> futures = new ArrayList<>(); 

     for (String url : buildUrlsList()) { 
      futures.add(executorService.submit(() -> processUrl(url))); 

     } 

     // iterate through the future 
     int total = 0; 
     for (Future<Integer> future : futures) { 
      total += future.get(); 
     } 
     System.out.println(total); 
     Date end = new Date(); 
     System.out.println((end.getTime() - start.getTime())/1000); 

    } 

    public static List<String> buildUrlsList() { 
     return Arrays.asList("url1", "url2", "url3", "url4", "url5", "url6", "url7", "url8", "url9"); 

    } 

} 
+0

將信息移入不同進程的時間可能是瓶頸。爲了獲得法國美食,你不會一路飛往法國。你花了很多時間來複制數據,沒有足夠的時間去做實際的工作。 –

+1

好吧,它是關於*你如何*基準和*你基準。應該看看這裏:http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java。 – Eugene

+1

除了我們不知道什麼'Sample :: processUrl'正在做什麼,所以不能真正幫助你。但很明顯,在一臺8核機器上有100個線程的「ExecutorServce」並不好。 – Eugene

回答

5

解釋很簡單。你有8個內核,所以parallelStream()通常可以在8個線程中並行工作。他們都立即抓住一個任務,他們都睡了5秒鐘。然後其中一人接受下一個(第九)任務,並且再睡5秒鐘。然後處理完成。這意味着〜5秒(8線程)+ 5秒(1線程)=總共10秒。但讓我們看看這是行動。我稍微修改代碼:

public static int processUrl(String url) { 

    try { 
     Thread.sleep(5000); 
    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    System.out.println("T[" + Thread.currentThread().getId() + "] finished @[" + System.currentTimeMillis()/1000 + "]"); 
    return url.length(); 
} 

與並行流你可能會得到類似的輸出:

T[1] finished @[1494267500] 
T[12] finished @[1494267500] 
T[17] finished @[1494267500] 
T[13] finished @[1494267500] 
T[14] finished @[1494267500] 
T[16] finished @[1494267500] 
T[11] finished @[1494267500] 
T[15] finished @[1494267500] 
T[12] finished @[1494267505] 
36 
10 

注意的是,同一個線程T [12]完成任務的兩倍,並完成5秒在8個任務的第一輪之後。

使用線程執行器,您已創建100個線程。所以9個線程抓住每一個任務的執行時間將是大約5秒,因爲線程池不會被耗盡:

T[14] finished @[1494267783] 
T[11] finished @[1494267783] 
T[19] finished @[1494267783] 
T[17] finished @[1494267783] 
T[12] finished @[1494267783] 
T[16] finished @[1494267783] 
T[13] finished @[1494267783] 
T[15] finished @[1494267783] 
T[18] finished @[1494267783] 
36 
5 

注意,有與同沒有線程ID-S在這裏。 (這是不是建議選擇一個固定池的通用數量的線程:-)我只是闡述你的實際問題)。

實驗用的調度和分配僅有8線程:

ExecutorService executorService = Executors.newFixedThreadPool(8); 

然後執行時間可能是大致相同的,因爲線程池將被耗盡。如果URL-s僅爲8,而不是9,則您會注意到類似的性能。

OF COURSE無法保證此代碼在不同環境中的行爲相同。