java 8并行流需要更多时间

标签 java concurrency parallel-processing java-8

我正在尝试了解 Java 8 并行流。我写了下面的代码,首先使用 Executor 然后使用并行流。 似乎并行流花费的时间是 Executor 方法(5 秒)的两倍(10 秒)。在我看来,并行流也应该表现出类似的性能。知道为什么并行流需要双倍时间吗? 我的电脑有 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");

    }

}

最佳答案

解释很简单。您有 8 个核心,因此 parallelStream() 通常可以将工作并行化为 8 个线程。他们都立即捕获了一个任务,然后都睡了 5 秒钟。然后其中一个接受下一个(第 9 个)任务,它再睡 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] 完成了两次任务,并在 8 个任务的第一“轮”后 5 秒完成。

您已经使用线程执行器创建了 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 的线程。 (这不是为固定池选择通用线程数的建议:-)我只是在详细说明您的实际问题)。

使用调度程序进行试验并仅分配 8 个线程:

ExecutorService executorService = Executors.newFixedThreadPool(8);

然后执行时间可能大致相同,因为线程池将被耗尽。如果 URL-s 只是 8 个而不是 9 个,您会注意到类似的性能。

当然无法保证此代码在不同环境中的行为相同。

关于java 8并行流需要更多时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43853518/

相关文章:

java - java中String变量的并发访问

JavaFX 在初始化期间使用解析的变量

concurrency - 实现 From<std::sync::PoisonError> 时为 "error: underscore lifetimes are unstable"

java - 在并发环境中定义字段

c# - 线程并发数据库查询是否安全?

concurrency - 与withPool并行

java - 为什么 Java "String"类型用大写字母写,而 "int"不是?

java - 为什么类型需要定义两次?

Python:并行化任何/所有语句

.net - Parallel.ForEach - 优雅的取消