JD.SYNC.DCL
JD.SYNC.DCL は、ダブルチェックロッキングが検出された場合に発生します (下記の最初の例を参照)。
脆弱性とリスク
ダブルチェックロッキングは、幅広く取り上げられ、マルチスレッド環境での遅延初期化 (Lazy Initialization) の効率的な手法として使用されています。残念ながら、J2SE 1.4 (および、それ以前のバージョン) に実装した場合、プラットフォーム非依存の方法では動作が保証されていません。ダブルチェックロッキングのイディオムはオブジェクトへの参照に対して使用することができませんが、32 ビットのプリミティブ値 (int、float など) に対しては動作可能です。ただし、long や double に対しては動作できません。64 ビットプリミティブの非同期読み取り/書き込みの原子性が保証されていないためです。
特定の状況下において、コンパイラによって生成されたコードに不完全に初期化されたオブジェクトが含まれ、文の前にこのオブジェクトがフィールドに代入される場合、ダブルチェックロッキングのパターンは動作せず、オブジェクトの初期化がファイナライズされます。したがって、他のスレッドは、デフォルトのフィールド値を持つオブジェクトへの非 NULL 参照が見えることになります。コンパイラがこれらの文の再順序化を行わない場合でも、マルチプロセッサシステムではプロセッサまたはメモリシステムが、別のプロセッサで実行しているスレッドで認識されたようにこれらの文の再順序化を行います。
軽減と防止
J2SE 5.0 以降、この問題は修正されています。現在は、'volatile' キーワードにより、複数のスレッドがシングルトンインスタンスを正しく処理することが保証されます。作成されたシングルトンが static の場合、ソリューションは、独立したクラスの static フィールドとしてシングルトンを定義することになります。The semantics of Java のセマンティックスは、フィールドが参照されるまで初期化されないことを保証するとともに、そのフィールドにアクセスするすべてのスレッドがそのフィールドの初期化結果のすべてを見ることができるように保証します。その他の場合、ダブルチェックなしでも同期化は使用できますが、メソッドの同期化はパフォーマンスを大幅に低下させることがある点に注意する必要があります (下記の 2 番目の例を参照)。詳細については、以下を参照してください。Wikipedia 記事: ダブルチェック済みロッキング。
例 1
class MyClass {
MyClass son;
void doubleCheckedLocking() {
if (son==null) {
synchronized (this) {
if (son==null) {
son = new MyClass();
}
}
}
}
}
JD.SYNC.DCL が 14 行目に対して報告されています。'son' に対するダブルチェックロッキングは、目標を達成しないイディオムです。
例 2
class MyClass {
private MyClass son = null;
public synchronized void doubleCheckedLocking() {
if (son == null)
son = new MyClass();
}
}
メソッドは同期化されており、ダブルチェックの必要はありません。