【问题标题】:Parallel stream creates only one thread and gives result as fast as normal stream并行流只创建一个线程,并以与普通流一样快的速度给出结果
【发布时间】:2015-06-12 21:37:22
【问题描述】:

下面是我尝试在并行流和普通流中处理从文件中读取的行的代码。令人惊讶的是,并行流比普通流没有任何改进。我在这里错过了什么吗?

Files.walk(Paths.get(tweetFilePath + LocalDate.now())).forEach(
            filePath -> {
                if (Files.isRegularFile(filePath) && !filePath.toString().endsWith(".DS_Store")) {
                    long startTime = System.currentTimeMillis();
                    try {

                        Files.lines(filePath).parallel().forEach(line -> {
                                try {
                                    System.out.println(line);

                                } catch (Exception e) {
                                    System.out.println("Not able to crunch"+ e);
                                }

                        });
                    } catch (Exception e) {
                        System.out.println("Bad line in file ");
                    }finally {
                        System.out.println("total time required:" + (System.currentTimeMillis() - startTime));

                    }   
                }
            });

【问题讨论】:

  • 在这种情况下,瓶颈在于 I/O 源 - 如果您有一个物理硬盘驱动器,那么您可以随心所欲地并行化,它不会变得更快......
  • 也许不是在行上并行化,而是在文件上并行化

标签: multithreading java-8 java-stream


【解决方案1】:

目前看来,Files.lines 是线性读取文件,因此并行调用无法将源流拆分为子流进行并行处理。

请参阅此处查看details。相关部分引用如下:

如果我的来源基于 IO 怎么办?

目前,基于 JDK IO 的 Stream 源(例如 BufferedReader.lines()) 主要面向顺序使用, 在元素到达时一个接一个地处理它们。机会存在于 支持缓冲 IO 的高效批量处理,但这些 目前需要定制开发 Stream 源、Spliterator、 和/或收藏家。未来的JDK可能会支持一些常见的形式 发布。

【讨论】:

  • 这似乎是正确的。我将文件中的所有行收集到一个列表中,然后在其上并行流式传输。现在明显快了。谢谢!
【解决方案2】:

第一个问题是Files.lines 并行化很糟糕,尤其是对于短于 1024 行的文件。查看this问题了解详情。如果您事先知道您的文件足够短以适合内存,最好先顺序读取到List

Files.readAllLines(filePath, StandardCharsets.UTF_8).parallelStream()...

我对如何improve this 有一些想法,但这仍然不是理想的解决方案。事实上,如果您甚至无法估计输入流中的元素数量,Stream API 并行化就非常无效。

第二个问题是您的forEach 操作。这里你只使用System.out,所以所有线程都会尝试写入同一个PrintStream,争夺同一个资源,因此大部分时间都花在等待锁释放上。在内部,它使用BufferedWriter,所有写入都是同步的。如果您不在并行操作中使用共享资源,您可能会从并行化中受益。

顺便说一句,Files.linesBufferedReader 上创建了一个流。最好用try-with-resources 语句来管理它。否则文件只会在底层FileInputStream 对象被垃圾回收时才会关闭,因此您可能偶尔会遇到“打开的文件过多”等错误。

【讨论】:

    猜你喜欢
    • 2019-12-06
    • 2020-10-11
    • 1970-01-01
    • 1970-01-01
    • 2014-10-31
    • 1970-01-01
    • 2016-05-15
    • 1970-01-01
    • 2015-09-23
    相关资源
    最近更新 更多