データレースは保証行動ではありませんが、次のコード
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()
);
は、再現性の番号を印刷しますサプライヤがスレッドセーフではない場合に、更新が不足している可能性があることを実証しています。
結果の評価に必要以上に多くの要素がサプライヤに問い合わせされる可能性もあります。例えば。
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の異なる番号を使用できます。
私は2つの例のどちらにも言及していません。さて、最初の並べ替えは意味があります。共有変数は複数のスレッドから更新されます。つまり、サプライヤはスレッドセーフでなければなりませんが、2番目は 'limit' perseです。 – Eugene
@Eugene:サプライヤがスレッドセーフであっても、消費された要素に対してのみクエリが実行されることを想定してはいけません。これはすべての短絡動作(および無限ストリームには短絡動作が必要です)に適用されます。 'limit'は単なる明らかな例です。そして、 'generate'によって返されたストリームが*順序付けられていない*であることを見落とすのは簡単です。そのメソッドを見ると誰かの心に入ってくる多くのユースケースが、それについてもう一度考えるときに実際には不適切なものになるかもしれません... – Holger
@Holger I上記の説明を理解してください、それは微妙で重要なポイントです – MyStackRunnethOver