【问题标题】:For Stream.generate(foo).parallel(), does foo have to be thread safe?对于 Stream.generate(foo).parallel(),foo 必须是线程安全的吗?
【发布时间】:2017-12-08 01:09:57
【问题描述】:

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

如果我写Stream.generate(foo).parallel()foo()会被异步调用吗? IE。是串行生成对象,并传递给并行线程,还是多个线程根据需要调用foo()来生成对象?

【问题讨论】:

    标签: java parallel-processing java-stream


    【解决方案1】:

    供应商将从多个线程中调用,您可以通过快速实验观察到:

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

    【讨论】:

    • 被多个线程调用并不一定意味着被同时调用。比较,例如forEachOrdered 可以从不同的线程调用其操作,但不能同时调用。所以这个测试不足以确定供应商是否必须是线程安全的。 (虽然,在这种情况下,供应商确实会被同时评估,并且必须是线程安全的)。
    【解决方案2】:

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

    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 的数字,这表明当供应商不是线程安全时,确实可能会发生丢失更新。

    请注意,也有可能向供应商询问比结果评估所需更多的元素。例如

    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 个不同的数字。

    【讨论】:

    • 我在这两个例子中都没有抓住重点。嗯,第一种是有道理的,一个共享变量是从多个线程更新的,这意味着 Supplier 必须是线程安全的,但第二种是关于 limit 本身,还是只是为了强制执行第一点?跨度>
    • @Eugene:即使你的供应商是线程安全的,你也不能假设它只会被查询消耗的元素。这适用于所有短路操作(无限流需要短路操作才能完成)。 limit 只是一个明显的例子。而且很容易忽略generate 返回的流是无序的,所以很多人在看到该方法时想到的用例可能在再次考虑时实际上并不合适…
    • @Holger 我很欣赏上面的澄清,这是一个微妙而重要的观点
    猜你喜欢
    • 2019-05-18
    • 1970-01-01
    • 2017-12-09
    • 2011-01-06
    • 2011-01-03
    • 1970-01-01
    • 2011-10-05
    • 1970-01-01
    • 2016-02-18
    相关资源
    最近更新 更多