2015年4月7日付けのAndreiPanginによるこの問題の優れた説明があります。ここから入手できます。でが、ロシア語で書かれています(とにかくコードサンプルを確認することをお勧めします-それらは国際的です)。一般的な問題は、クラスの初期化中のロックです。
記事からの引用は次のとおりです。
JLSによると、すべてのクラスには、初期化中にキャプチャされる一意の初期化ロックがあります。初期化中に他のスレッドがこのクラスにアクセスしようとすると、初期化が完了するまでロックでブロックされます。クラスが同時に初期化されると、デッドロックが発生する可能性があります。
整数の合計を計算する簡単なプログラムを作成しましたが、何を出力する必要がありますか?
public class StreamSum {
static final int SUM = IntStream.range(0, 100).parallel().reduce((n, m) -> n + m).getAsInt();
public static void main(String[] args) {
System.out.println(SUM);
}
}
parallel()
ラムダを削除するか、Integer::sum
callに置き換えます-何が変わりますか?
ここで再びデッドロックが発生します[以前の記事でクラス初期化子のデッドロックの例がいくつかありました]。parallel()
ストリーム操作は別のスレッドプールで実行されるためです。これらのスレッドは、クラスprivate static
内のメソッドとしてバイトコードで記述されたラムダ本体を実行しようとしStreamSum
ます。ただし、このメソッドは、ストリームの完了の結果を待機するクラスstaticinitializerが完了する前に実行することはできません。
さらに驚異的なのは、このコードは環境によって動作が異なることです。シングルCPUマシンで正しく動作し、マルチCPUマシンでハングする可能性があります。この違いは、Fork-Joinプールの実装に起因します。パラメータを変更して自分で確認できます-Djava.util.concurrent.ForkJoinPool.common.parallelism=N