AtomicInteger recordNumber = new AtomicInteger();
Files.lines(inputFile.toPath(), StandardCharsets.UTF_8)
.map(record -> new Record(recordNumber.incrementAndGet(), record))
.parallel()
.filter(record -> doSomeOperation())
.findFirst()
当我写这篇文章时,我假设线程将仅在map调用中产生,因为在map之后放置了parallel。但是文件中的某些行每次执行都会获得不同的记录号。
我阅读了Java流的正式文档和一些网站,以了解流是如何工作的。
几个问题:
Java并行流基于SplitIterator进行工作,它由ArrayList,LinkedList等每个集合实现。当我们从这些集合构造并行流时,将使用相应的split迭代器对集合进行拆分和迭代。这解释了为什么并行性发生在原始输入源(文件行)级别而不是映射结果(即Record pojo)。我的理解正确吗?
就我而言,输入是文件IO流。将使用哪个拆分迭代器?
我们放置
parallel()
在管道中的位置无关紧要。原始输入源将始终被分割,其余的中间操作将被应用。在这种情况下,Java不应允许用户在管道中除原始源之外的任何地方进行并行操作。因为,它为那些不知道java流内部工作方式的人提供了错误的理解。我知道
parallel()
操作将为Stream对象类型定义,因此,它是以这种方式工作的。但是,最好提供一些替代解决方案。在上面的代码片段中,我试图将行号添加到输入文件中的每个记录,因此应该对它进行排序。但是,我想
doSomeOperation()
并行应用,因为它是繁重的逻辑。一种实现方式是编写自己的自定义拆分迭代器。还有其他办法吗?
Stream
直接在界面中提供,并且由于很好的级联,每个操作又返回Stream
了。想象有人想要给您一个Stream
但已经应用了一些类似的操作map
。作为用户,您仍然希望能够决定是否使其并行执行。因此parallel()
,尽管该流已经存在,但您仍然必须可以仍然呼叫。
flatMap
或不执行线程不安全的方法或类似方法大大增加了大小。
Path
本地文件系统上并且您使用的是最新的JDK时,分离器将具有比批处理1024的倍数更好的并行处理能力。但是在某些findFirst
情况下,均衡的拆分甚至可能适得其反……
parallel()
只不过是应用于基础流对象的常规修饰符请求。请记住,如果您不对管道应用最终操作,即只要没有“执行”,就只有一个源流。话虽如此,您基本上只是在质疑Java设计选择。这是基于意见的,我们对此无能为力。