第一個實現比多處理器中的其他實現更安全。以下規則保證了爲什麼第一個實現是安全的。之前發生-的程序的任何其它動作(其他 比缺省寫入)
任何對象的默認初始化:
由於JLS 17.4.5 Happens-before Order
定義一個規則。
因此當有人調用get()方法時,instance
字段被完全初始化。
第二種方案被稱爲lazy initialization
,但它不是正確的code.In爲了使用lazy initialization
盯着程序快,是一個很好的做法如果GameManager
初始化花費更多的時間。
隨着第二次實現,有兩種方法可以使GameManager
線程安全。
首先,你可以使該方法instance
同步的,就像這樣:
public synchronized static GameManager instance(){
if(_instance == null) _instance = new GameManager();
return _instance;
}
但它不是高性能的,因爲每次調用將與多線程方法進行同步。
第二,你可以在方法instance
,使用double-check
並聲明_instance
爲volatile
,下面的代碼:
static volatile GameManager _instance;
public static GameManager instance(){
if (_instance== null) { //0
synchronized(GameManager.class) { //1
if (_instance == null) //2
_instance= new GameManager(); //3
}
}
return o;
}
這個實現的代碼是正確和高性能。
爲什麼field
_instance should be volatile?Let's see the following situation with no
揮發性,there is two thread called
線程1 and
線程2 ,and they all call the method
實例()`:
- 線程1,在點0在上面的代碼運行
- 線程1檢查
_instance
爲空,沒有人獲取鎖,所以它可以運行在點3
- 與代碼
_instance= new GameManager(); //3
,由於Java Memory Model
中的指令重新排序,JVM
可以拆分儀器'interpreter or
jit .Let's see what happened with that code.The instance has been constructed,the constructing instance is just be allocated memory(like the instruction
jitter .Let's see what happened with that code.The instance has been constructed,the constructing instance is just be allocated memory(like the instruction
new in
JVM ,not in
JAVA ),and then assign to the field
_instance`,但注意實例沒有完全初始化(例如,沒有使用默認值初始化字段或調用靜態構造方法);
- thread2運行在0點,並檢查
_instance
不爲空,因此它會返回未完全初始化的_instance
,並且該實例不是 值得信賴。
如果_instance
是volatile
,該volatile
可以保證_instance
完全初始化時,有人讀it.See的17.4.5 Happens-before Order
,有一個規則:
寫的揮發性場(§8.3.1.4 )之前發生的是field.t
第一個在構造函數中實例化。 (新的GameManager()去構造函數)。但他們都是一樣的。 –
如果你打算在鎖定等多線程上下文中使用這個單例,那麼這個單例的實例化應該小心。這就是爲什麼他們使用第二個實現來使用鎖,惰性(在C#中)等。 –
在Java中,第二個版本並不保證該字段的單一實例化。如果該類可以併發訪問,則應該使用持有者類來執行此操作(或者同步更昂貴的方法以避免) – davidxxx