Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.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 在并行转换流时如何使用收集器_Java_Multithreading_Java 8_Java Stream_Collectors - Fatal编程技术网

Java 在并行转换流时如何使用收集器

Java 在并行转换流时如何使用收集器,java,multithreading,java-8,java-stream,collectors,Java,Multithreading,Java 8,Java Stream,Collectors,实际上我试图回答这个问题。所以我认为这个收集器不能很好地并行工作: private static Collector<String, ?, List<String>> oddLines() { int[] counter = {1}; return Collector.of(ArrayList::new, (l, line) -> { if (counter[0] % 2 == 1) l.add

实际上我试图回答这个问题。所以我认为这个收集器不能很好地并行工作:

private static Collector<String, ?, List<String>> oddLines() {
    int[] counter = {1};
    return Collector.of(ArrayList::new,
            (l, line) -> {
                if (counter[0] % 2 == 1) l.add(line);
                counter[0]++;
            },
            (l1, l2) -> {
                l1.addAll(l2);
                return l1;
            });
}
private静态收集器oddLines(){
int[]计数器={1};
返回收集器.of(ArrayList::new,
(左行)->{
如果(计数器[0]%2==1)l.add(行);
计数器[0]++;
},
(l1,l2)->{
l1.addAll(l2);
返回l1;
});
}
但它是有效的

编辑:它实际上不起作用;我被这样一个事实愚弄了:我的输入集太小,无法触发任何并行性;请参见评论中的讨论

我想这行不通,因为我想到了下面两个处决计划


1.
计数器
数组在所有线程之间共享。 线程t1读取流的第一个元素,因此满足if条件。它将第一个元素添加到其列表中。然后在他有时间更新数组值之前停止执行

线程t2表示从流的第4个元素开始,将其添加到列表中。所以我们最终得到了一个非通缉的元素

当然,因为这个收藏家似乎很管用,我想它不是那样管用的。而且这些更新也不是原子的


2.每个线程都有自己的数组副本 在这种情况下,更新没有更多的问题,但是没有什么可以阻止我,线程t2将不会从流的第4个元素开始。所以他也不是那样工作的


所以它似乎根本就不是这样工作的,这就引出了一个问题。。。收集器是如何并行使用的

有人能给我解释一下它是如何工作的,以及为什么我的收集器在并行运行时工作的吗

多谢各位

parallel()
源流传递到收集器中足以破坏逻辑,因为共享状态(
计数器)可能会从不同的任务中递增。您可以验证这一点,因为它永远不会返回任何有限流输入的正确结果:

    Stream<String> lines = IntStream.range(1, 20000).mapToObj(i -> i + "");
    System.out.println(lines.isParallel());
    lines = lines.parallel();
    System.out.println(lines.isParallel());

    List<String> collected = lines.collect(oddLines());

    System.out.println(collected.size());
这显然是错误的


正如@Holger在评论中正确指出的,当收集器指定
CONCURRENT
UNORDERED
时,可能会发生不同的竞争,在这种情况下,它们跨任务对单个共享集合进行操作(
ArrayList::new
,每个流调用一次),其中仅使用
parallel()
它将在每个任务的集合上运行累加器,然后使用定义的组合器组合结果

如果将特征添加到收集器中,由于单个集合中的共享状态,可能会出现以下结果:

false
true
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 73
    at java.util.ArrayList.add(ArrayList.java:459)
    at de.jungblut.stuff.StreamPallel.lambda$0(StreamPallel.java:18)
    at de.jungblut.stuff.StreamPallel$$Lambda$3/1044036744.accept(Unknown Source)
    at java.util.stream.ReferencePipeline.lambda$collect$207(ReferencePipeline.java:496)
    at java.util.stream.ReferencePipeline$$Lambda$6/2003749087.accept(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
    at java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250)
    at java.util.stream.Streams$RangeIntSpliterator.forEachRemaining(Streams.java:110)
    at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512)
    at java.util.stream.ForEachOps$ForEachTask.compute(ForEachOps.java:291)
    at java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:731)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:401)
    at java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:734)
    at java.util.stream.ForEachOps$ForEachOp.evaluateParallel(ForEachOps.java:160)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateParallel(ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:496)
    at de.jungblut.stuff.StreamPallel.main(StreamPallel.java:32)12386

事实上,这个收藏家工作只是巧合。它不适用于自定义数据源。考虑这个例子:

List<String> list = IntStream.range(0, 10).parallel().mapToObj(String::valueOf)
        .collect(oddLines());
System.out.println(list);

如果收集器工作正常,则不应打印任何内容。但有时它会打印。

仔细想想,如果收集器以非串行方式(或串行但顺序不同)输入元素,那么即使没有计数器,收集器也无法正常工作。事实证明,(2)是最准确的解释,但没有太多实现细节。如果选择简单的
Collections.toList
作为收集器。并行流将执行map/reduce以收集所有元素。它使用ForkJoin池在并行执行时提供fork/join工作。@JohnVint
toList
在线程之间不共享一个公共变量,因此我理解它是如何工作的。就在你有状态的时候。哦,对了。我感到惊讶的是,它当时起作用了。累加器肯定是在多个线程中执行的。@JohnVint是的,这是因为我的输入文件太小。事实上,我很高兴它没有产生预期的结果,这在某种程度上证实了我对执行的了解。为什么你坚持在你的答案中保留第一行,而在另一个答案的评论中保留第一行?通过阅读你的评论,我认为你明白没有这些特征,这些流仍然以不同的模式并行处理。如果你没有,请注意这句话完全错了。如果没有这两个标志,累加器可以并行运行,但将使用不同的容器,然后使用组合器功能组合这些容器。当指定
CONCURRENT
UNORDERED
时,可以在一个容器上并发调用累加器,而无需任何组合器调用。您是对的;)我以为我正在浏览顺序代码部分。@user2336315欢迎来到奇妙的竞速条件世界,我认为在大小更新之后,在内部调整大小以容纳新元素之前,添加了一个元素。它是。但请注意,您甚至不需要大型数据集来发现问题。这只是需要大数据的文件I/O情况。我设法用一个像
Stream.of(“1”、“2”、“3”).parallel().collect(oddLines())
这样简单的表达式生成错误的结果…
List<String> list = IntStream.range(0, 10).parallel().mapToObj(String::valueOf)
        .collect(oddLines());
System.out.println(list);
String data = IntStream.range(0, 10000).mapToObj(String::valueOf)
    .collect(Collectors.joining("\n"));
List<String> list = new BufferedReader(new StringReader(data)).lines().parallel()
    .collect(oddLines());
list.stream().mapToInt(Integer::parseInt).filter(x -> x%2 != 0)
    .forEach(System.out::println);