对于Stream.generate(foo).parallel(),foo必须是线程安全的吗?

时间:2017-12-08 01:09:57

标签: java parallel-processing java-stream

我正在使用Java 8 Streams。我有一个自定义函数foo(),它产生一个对象,我想并行传输它创建的对象。我知道foo()不是线程安全的。

如果我写Stream.generate(foo).parallel(),是否会以异步方式调用foo()?即将对象串行生成并传递给并行线程,还是多个线程根据需要通过调用foo()来生成对象?

2 个答案:

答案 0 :(得分:5)

您可以通过快速实验观察供应商的多个线程:

Stream.generate(() -> Thread.currentThread().getId())
    .parallel()
    .limit(100000)
    .distinct()
    .forEach(System.out::println);

答案 1 :(得分:3)

虽然数据争用不是保证行为,但以下代码

System.out.println(
    Stream.generate(new Supplier<Integer>() {
        int i; @Override public Integer get() { return i++; }
    }).parallel()
      .limit(10_000)
      .collect(BitSet::new, BitSet::set, BitSet::or)
      .cardinality()
);

在我的环境中可重复地打印小于10,000的数字,证明当供应商不是线程安全时,确实可能发生缺失的更新。

请注意,供应商也可能会查询比结果评估所需的元素更多的元素。 E.g。

LongAdder adder = new LongAdder();
System.out.println(
    Stream.generate(new Supplier<Integer>() {
        int i; @Override public Integer get() { adder.increment(); return i++; }
    }).parallel()
      .limit(10_000)
      .collect(BitSet::new, BitSet::set, BitSet::or)
      .cardinality()
);
System.out.println("queried "+adder+" times");

通常报告大于10,000的大量查询,同时,由于数据竞争,结果报告的不同元素少于10,000个。

使供应商线程安全,将结果更改为10,000个不同元素的正确数量,但供应商可能仍然被查询超过10,000次,因此,结果不能保证完全包含0到9,999之间的数字,由于通过generate创建的流是无序,因此可以使用来自供应商的任何10,000个不同的数字。