Findbugは、間違った遅延初期化を使用していると言った。
public static Object getInstance() {
if (instance != null) {
return instance;
}
instance = new Object();
return instance;
}
ここには何も問題はありません。 findbugの間違った動作ですか、何か見落としていましたか?
Findbugは潜在的なスレッドの問題を参照しています。マルチスレッド環境では、現在のコードでシングルトンが複数回作成される可能性があります。
多くの読み物があります ここ ですが、説明に役立ちます。
ここでの競合状態はif check
にあります。最初の呼び出しで、スレッドはif check
に入り、インスタンスを作成して「インスタンス」に割り当てます。ただし、if check
とインスタンスの作成/割り当ての間で別のスレッドがアクティブになる可能性があります。割り当てがまだ行われていないため、このスレッドはif check
を渡すこともできます。したがって、2つ(または、より多くのスレッドが入った場合はそれ以上)のインスタンスが作成され、スレッドは異なるオブジェクトへの参照を持ちます。
あなたのコードは必要以上にやや複雑であるため、混乱している可能性があります。
編集:それは間違いなく他の人が投稿したスレッドの問題ですが、以下の参照のためにここにダブルロックチェックの実装を投稿すると思いました:
private static final Object lock = new Object();
private static volatile Object instance; // must be declared volatile
public static Object getInstance() {
if (instance == null) { // avoid sync penalty if we can
synchronized (lock) { // declare a private static Object to use for mutex
if (instance == null) { // have to do this inside the sync
instance = new Object();
}
}
}
return instance;
}
[〜#〜] note [〜#〜]:JohnKlehmのダブルロックチェックソリューションの方が優れています。歴史的な理由でこの回答をここに残します。
実際にあるはずです
public synchronized static Object getInstance() {
if (instance == null) {
instance = new Object();
}
return instance;
}
これを正しくするには、インスタンス化をロックする必要があります
LI:静的フィールドの誤った遅延初期化(LI_LAZY_INIT_STATIC)
このメソッドには、不揮発性静的フィールドの非同期の遅延初期化が含まれています。コンパイラまたはプロセッサは命令を並べ替えるため、メソッドが複数のスレッドから呼び出される場合、スレッドは完全に初期化されたオブジェクトを参照することを保証されません。フィールドを揮発性にして問題を修正できます。詳細については、Java Memory Model Webサイトを参照してください。
マルチスレッドの問題を見逃した、
private static Object instance;
public static synchronized Object getInstance() {
return (instance != null ? instance : (instance = new Object()));
}
サンプルを投稿してくれたJohn Klehmに感謝
また、同期ブロック内のオブジェクトインスタンスを直接割り当てようとする場合があります
synchronized (MyCurrentClass.myLock=new Object())
つまり.
private static volatile Object myLock = new Object();
public static Object getInstance() {
if (instance == null) { // avoid sync penalty if we can
synchronized (MyCurrentClass.myLock**=new Object()**) { // declare a private static Object to use for mutex
if (instance == null) { // have to do this inside the sync
instance = new Object();
}
}
}
return instance;
}
静的オブジェクトは同期されません。さらに、メソッドは遅延初期化ではありません。通常は、オブジェクトのマップを保持し、必要に応じて目的のマップを初期化します。したがって、必要な(呼び出される)ときに呼び出すのではなく、最初にすべてを初期化するわけではありません。
1.5以降:インスタンスは揮発性であり、youldはtmp変数を統合して、作成されたが初期化がまだ完了していないインスタンスを使用しないようにします。
private static volatile Object myLock = new Object();
private static volatile Object instance;
public static Object getInstance() {
if (instance == null) {
Object tmpObj;
synchronized (myLock) {
tmpObj = instance;
if (tmpObj == null) {
tmpObj = new Object();
}
}
instance = tmpObj;
}
return instance;
}