Java并行流-调用parallel()方法的顺序
当我写这篇文章时,我假设线程将只在map调用中产生,因为parallel放在map之后。但文件中的某些行每次执行都会得到不同的记录编号 我阅读了官方网站和一些网站来了解流是如何在引擎盖下工作的 有几个问题:Java并行流-调用parallel()方法的顺序,java,java-stream,Java,Java Stream,当我写这篇文章时,我假设线程将只在map调用中产生,因为parallel放在map之后。但文件中的某些行每次执行都会得到不同的记录编号 我阅读了官方网站和一些网站来了解流是如何在引擎盖下工作的 有几个问题: Java并行流基于,由ArrayList、LinkedList等每个集合实现。当我们从这些集合构造并行流时,将使用相应的拆分迭代器拆分和迭代集合。这解释了为什么并行性发生在原始输入源(文件行)级别,而不是map的结果(即recordpojo)。我的理解正确吗 在我的例子中,输入是一个文件I
- Java并行流基于,由ArrayList、LinkedList等每个集合实现。当我们从这些集合构造并行流时,将使用相应的拆分迭代器拆分和迭代集合。这解释了为什么并行性发生在原始输入源(文件行)级别,而不是map的结果(即recordpojo)。我的理解正确吗
- 在我的例子中,输入是一个文件IO流。将使用哪个拆分迭代器
- 我们将
放置在管道中的何处并不重要。原始输入源将始终被拆分,其余的中间操作将被应用 在这种情况下,Java不应允许用户将并行操作放置在管道中的任何位置,原始源位置除外。因为,对于那些不知道java流在内部如何工作的人来说,这是一种错误的理解。我知道parallel()
parallel()。但是,最好提供一些替代解决方案
- 在上面的代码片段中,我试图向输入文件中的每条记录添加行号,因此应该对其进行排序。但是,我想并行应用
,因为这是一个很重的逻辑。实现的一种方法是编写自己的自定义拆分迭代器。还有别的办法吗doSomeOperation()
在我的例子中,输入是一个文件IO流。将使用哪个拆分迭代器 查看源代码,我看到它使用
java.nio.file.FileChannelLinesSpliterator
将parallel()放在管道中的何处并不重要。原始输入源将始终被拆分,其余的中间操作将被应用 对。您甚至可以多次调用
parallel()
和sequential()
。最后一个会赢。当我们调用parallel()
时,我们为返回的流设置它;如上所述,所有操作都是按顺序或并行运行的
在这种情况下,Java不应该允许用户将并行操作放置在管道中的任何位置,除了原始源位置 这成了一个意见问题。我认为Zabuza为支持JDK设计师的选择提供了一个很好的理由
实现的一种方法是编写自己的自定义拆分迭代器。还有别的办法吗 这取决于你的行动
- 如果
是您真正的终端操作,那么您甚至不需要担心并行执行,因为对findFirst()
的调用不会太多(doSomething()
是短路)findFirst()
实际上可能会导致处理多个元素,而顺序流上的.parallel()
则会阻止这种情况findFirst()
- 如果终端操作没有创建太多数据,那么您可以使用顺序流创建
对象,然后并行处理结果:记录
这将并行执行AtomicInteger recordNumber = new AtomicInteger(); Files.lines(inputFile.toPath(), StandardCharsets.UTF_8) .map(record -> new Record(recordNumber.incrementAndGet(), record)) .parallel() .filter(record -> doSomeOperation()) .findFirst()
,而不将所有数据加载到内存中。但请注意,doSomeOperation()
需要考虑batchSize
Files.lines(…)
使用的实际拆分器取决于实现。在Java8(Oracle或OpenJDK)中,使用BufferedReader.lines()
,您总是可以获得相同的结果。在较新的JDK中,如果路径属于默认文件系统,并且字符集是此功能支持的文件系统之一,则会得到一个带有专用拆分器实现的流,即java.nio.file.FileChannelLinesSpliterator
。如果不满足先决条件,则得到与BufferedReader.lines()
相同的结果,它仍然基于BufferedReader
中实现的迭代器,并通过Spliterators.spliteratorUnknownSize
包装
您的特定任务最好使用自定义的拆分器来处理,该拆分器可以在并行处理之前在源位置执行行号,以允许后续的并行处理不受限制
AtomicInteger recordNumber = new AtomicInteger();
final int batchSize = 10;
try(BufferedReader reader = Files.newBufferedReader(inputFile.toPath(),
StandardCharsets.UTF_8);) {
Supplier<List<Record>> batchSupplier = () -> {
List<Record> batch = new ArrayList<>();
for (int i = 0; i < batchSize; i++) {
String nextLine;
try {
nextLine = reader.readLine();
} catch (IOException e) {
//hanlde exception
throw new RuntimeException(e);
}
if(null == nextLine)
return batch;
batch.add(new Record(recordNumber.getAndIncrement(), nextLine));
}
System.out.println("next batch");
return batch;
};
Stream.generate(batchSupplier)
.takeWhile(list -> list.size() >= batchSize)
.map(list -> list.parallelStream()
.filter(record -> doSomeOperation())
.collect(Collectors.toList()))
.flatMap(List::stream)
.forEach(System.out::println);
}
公共静态流记录(路径p)引发IOException{
LineNoSpliterator sp=新的LineNoSpliterator(p);
返回StreamSupport.stream(sp,false).onClose(sp);
}
私有静态类
AtomicInteger recordNumber = new AtomicInteger();
final int batchSize = 10;
try(BufferedReader reader = Files.newBufferedReader(inputFile.toPath(),
StandardCharsets.UTF_8);) {
Supplier<List<Record>> batchSupplier = () -> {
List<Record> batch = new ArrayList<>();
for (int i = 0; i < batchSize; i++) {
String nextLine;
try {
nextLine = reader.readLine();
} catch (IOException e) {
//hanlde exception
throw new RuntimeException(e);
}
if(null == nextLine)
return batch;
batch.add(new Record(recordNumber.getAndIncrement(), nextLine));
}
System.out.println("next batch");
return batch;
};
Stream.generate(batchSupplier)
.takeWhile(list -> list.size() >= batchSize)
.map(list -> list.parallelStream()
.filter(record -> doSomeOperation())
.collect(Collectors.toList()))
.flatMap(List::stream)
.forEach(System.out::println);
}
public static Stream<Record> records(Path p) throws IOException {
LineNoSpliterator sp = new LineNoSpliterator(p);
return StreamSupport.stream(sp, false).onClose(sp);
}
private static class LineNoSpliterator implements Spliterator<Record>, Runnable {
int chunkSize = 100;
SeekableByteChannel channel;
LineNumberReader reader;
LineNoSpliterator(Path path) throws IOException {
channel = Files.newByteChannel(path, StandardOpenOption.READ);
reader=new LineNumberReader(Channels.newReader(channel,StandardCharsets.UTF_8));
}
@Override
public void run() {
try(Closeable c1 = reader; Closeable c2 = channel) {}
catch(IOException ex) { throw new UncheckedIOException(ex); }
finally { reader = null; channel = null; }
}
@Override
public boolean tryAdvance(Consumer<? super Record> action) {
try {
String line = reader.readLine();
if(line == null) return false;
action.accept(new Record(reader.getLineNumber(), line));
return true;
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
@Override
public Spliterator<Record> trySplit() {
Record[] chunks = new Record[chunkSize];
int read;
for(read = 0; read < chunks.length; read++) {
int pos = read;
if(!tryAdvance(r -> chunks[pos] = r)) break;
}
return Spliterators.spliterator(chunks, 0, read, characteristics());
}
@Override
public long estimateSize() {
try {
return (channel.size() - channel.position()) / 60;
} catch (IOException ex) {
return 0;
}
}
@Override
public int characteristics() {
return ORDERED | NONNULL | DISTINCT;
}
}
IntStream.rangeClosed (1,20).peek(a->System.out.print(a+" "))
.map(a->a + 200).sum();
System.out.println();
IntStream.rangeClosed(1,20).peek(a->System.out.print(a+" "))
.map(a->a + 200).parallel().sum();