コード:
class Singleton{
private volatile static Singleton instance = null;
private Singletion{}
public static Singleton getInstance(){
if(instance==null){ //#1
synchronized(Singleton.class){ //#2
if(instance==null){ //#3
instance = new Singletion(); //#4
}else{
System.out.println("test"); //#5
}
}
}
return instance;
}
}
シングルトンオブジェクトがvolatileキーワードを追加する必要があるのはなぜですか?2つの理由があります:
(1)可視性を確保します。
如果 "private static Singleton instance = null;":
1. Thread1が#1に入り、thread1のインスタンスがnull、thread1がCPUリソースをthread2に放棄します
。2。Thread2が#1に入り、thread2のインスタンスがnull、thread2がCPUリソースをthread1に
放棄します。3。Thread1が#2を実行します。#3、#4を順番に、そして最後にthread1でインスタンスをインスタンス化します。Thread1は実行を終了し、CPUリソースをthread2に渡します
。4。Thread2は#1を実行し、#1で取得されたインスタンスがまだnullであるため(そしてthread1からの最新のインスタンスが時間内に取得されなかったため)、#3に到達すると、したがって、Thread2は引き続き#3、#4を実行します
。5。最後に、thread1とthread2の両方がインスタンスをインスタンス化します。
如果 "private volatile static Singleton instance = null;":
1. Thread1が#1に入ると、thread1のインスタンスはnullになり(Javaメモリモデルはinstance(null)をメインメモリからthread1にコピーします)、thread1はCPUリソースをthread2に放棄します
。2。Thread2は#1に入ります。 thread2のインスタンスがnull(javaメモリモデルはinstance(null)をメインメモリからthread2にコピーします)、thread2はCPUリソースをthread1に放棄し
ます。3。Thread1は#2、#3、#4を順番に実行し、最後に、インスタンスはthread1でインスタンス化されます(これは揮発性の変更された変数であるため、メインメモリ内の変数にすぐに同期されます)。thread1が実行された後、CPUリソースをthread2に放棄します
。4。thread2、次に#1が実行され、#3に達すると、インスタンス(!= null)がメインメモリから再びコピーされるため、thread2はに直接実行されます。 #
5。5。最後に、thread2はインスタンスを繰り返しインスタンス化しなくなりました。
(2)再注文を防ぐ:
シングルトンモードでは、Instance instance = new Instance();はアトミック操作ではなく、アトミック命令の3つのステップに分割できます。
1.メモリアドレスを割り当てます。
2.新しいインスタンスオブジェクト。
3.メモリアドレスをインスタンスに割り当てます。
CPUが実行効率を向上させるために、これら3つの操作のシーケンスは123または132にすることができます。順序が132の場合、メモリアドレスがinstに割り当てられていると、シングルトンオブジェクトはinstが指すメモリアドレスで新しいものではありません。この時点で、インスタンスを取得すると、実際には空になり、nullポインタ例外が発生します。報告されます。