并行流只创建一个线程,并提供与普通流一样快的结果

时间:2015-06-12 21:37:23

标签: multithreading java-8 java-stream

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

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));

                    }   
                }
            });

2 个答案:

答案 0 :(得分:1)

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

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

  

如果我的来源基于IO?

,该怎么办?      

目前,基于JDK IO的Stream源(例如   BufferedReader.lines())主要用于顺序使用,   他们到达时逐个处理元素。机会存在   支持缓冲IO的高效批量处理,但这些   目前需要自定义开发Stream源,Spliterators,   和/或收藏家。未来的JDK可能会支持一些常见的表单   版本。

答案 1 :(得分:1)

第一个问题是Files.lines并行化严重,特别是对于短于1024行的文件。有关详细信息,请查看this问题。如果您事先知道您的文件足够短以适合内存,那么最好先按顺序读取List

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

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

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

顺便说一句,Files.lines会在BufferedReader上创建一个流。最好使用try-with-resources语句来管理它。否则,仅当基础FileInputStream对象被垃圾收集时才会关闭文件,因此您可能偶尔会出现“太多打开文件”等错误。