下面是我尝试在并行流和普通流中处理文件读取行的代码。令人惊讶的是,并行流与普通流相比没有任何改进。我在这里错过了什么吗?
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));
}
}
});
答案 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
对象被垃圾收集时才会关闭文件,因此您可能偶尔会出现“太多打开文件”等错误。