Singleton 作成時のダブルチェック
Singleton 作成時のダブルチェックロッキングは動作が保証されていません。
マルチスレッド絡みのバグなので、障害が発生しても非常に見つけにくい物です。
ダブルチェックロッキングを使用した例
public static SingletonTest getInstance() {
if (instance_ == null) {
synchronized (SingletonTest.class) { ・・・・・・1
if (instance_ == null) ・・・・・・2
instance_ = new SingletonTest(); ・・・・・・3
}
}
return instance_;
}
期待動作
ダブルチェックロッキングを利用してる人の期待している動作は
スレッド 1 |
スレッド 2 |
getInstance() に入る |
|
↓ |
|
instance_ が null の為 1 に入る |
|
↓ |
|
切り替え → |
getInstance() に入る |
|
↓ |
|
instance_ が null の為 1 に入るがブロックされる |
|
↓ |
2 でまだ instance_ が null の為
3 で new して instance_ に代入
|
← 切り替え |
↓ |
|
return でインスタンスの取得 |
|
↓ |
|
切り替え → |
1 でロックを取得して、2 の評価を行う |
|
↓ |
|
instance_ は null でない為スレッド 1 と同じインスタンスの取得 |
実際にはこのように動作しない可能性があります。どこが問題かと言うと、3 のインスタンスの生成部で、
Java のメモリー・モデルでは、SingletonTest のコンストラクターが実行される前に、
instance_ が null でなくなる可能性があります。
バグが顕著化する場合の動作
スレッド 1 |
スレッド 2 |
getInstance() に入る |
|
↓ |
|
instance_ が null の為 1 に入る |
|
↓ |
|
3 で instance_ を null でなくするが、コンストラクタは未実行 |
|
↓ |
|
切り替え → |
getInstance() に入る |
|
↓ |
|
instance_ が null でない為初期化されていないインスタンスを取得する。
Error 発生
|
|
↓ |
コンストラクタを実行して初期化する。 |
← 切り替え |
Java で Singleton を実現する
Singleton を作成するときはこんな感じで書くと正常に動作します。
public final class SingletonTest {
private static SingletonTest instance_ = new SingletonTest();
public static SingletonTest getInstance() {
return instance_;
}
private SingletonTest() {
}
}
|