Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/366.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java并行流-调用parallel()方法的顺序_Java_Java Stream - Fatal编程技术网

Java并行流-调用parallel()方法的顺序

Java并行流-调用parallel()方法的顺序,java,java-stream,Java,Java Stream,当我写这篇文章时,我假设线程将只在map调用中产生,因为parallel放在map之后。但文件中的某些行每次执行都会得到不同的记录编号 我阅读了官方网站和一些网站来了解流是如何在引擎盖下工作的 有几个问题: Java并行流基于,由ArrayList、LinkedList等每个集合实现。当我们从这些集合构造并行流时,将使用相应的拆分迭代器拆分和迭代集合。这解释了为什么并行性发生在原始输入源(文件行)级别,而不是map的结果(即recordpojo)。我的理解正确吗 在我的例子中,输入是一个文件I

当我写这篇文章时,我假设线程将只在map调用中产生,因为parallel放在map之后。但文件中的某些行每次执行都会得到不同的记录编号

我阅读了官方网站和一些网站来了解流是如何在引擎盖下工作的

有几个问题:

  • Java并行流基于,由ArrayList、LinkedList等每个集合实现。当我们从这些集合构造并行流时,将使用相应的拆分迭代器拆分和迭代集合。这解释了为什么并行性发生在原始输入源(文件行)级别,而不是map的结果(即recordpojo)。我的理解正确吗

  • 在我的例子中,输入是一个文件IO流。将使用哪个拆分迭代器

  • 我们将
    parallel()
    放置在管道中的何处并不重要。原始输入源将始终被拆分,其余的中间操作将被应用

    在这种情况下,Java不应允许用户将并行操作放置在管道中的任何位置,原始源位置除外。因为,对于那些不知道java流在内部如何工作的人来说,这是一种错误的理解。我知道
    parallel()。但是,最好提供一些替代解决方案

  • 在上面的代码片段中,我试图向输入文件中的每条记录添加行号,因此应该对其进行排序。但是,我想并行应用
    doSomeOperation()
    ,因为这是一个很重的逻辑。实现的一种方法是编写自己的自定义拆分迭代器。还有别的办法吗

这解释了为什么并行性发生在原始输入源(文件行)级别,而不是map的结果(即recordpojo)

整个流要么是并行的,要么是顺序的。我们不选择要顺序或并行运行的操作子集

当终端操作启动时,流管道将根据调用它的流的方向顺序或并行执行。[…]当终端操作启动时,流管道将根据调用它的流的模式顺序或并行执行

正如您所提到的,并行流使用拆分迭代器。显然,这是在操作开始运行之前对数据进行分区


在我的例子中,输入是一个文件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
    需要考虑


最初的流设计包含了支持具有不同并行执行设置的后续管道阶段的想法,但这一想法已被放弃。API可能起源于此时,但另一方面,强制调用方对并行或顺序执行做出单一明确决策的API设计将更加复杂

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