2017-06-18 76 views
1

我有問題來理解這兩個函數之間的區別。他們是不是都在實施單身人士?如果是的話,那麼互惠互利是什麼。這兩個單例實現有什麼區別?

private static GameManager instance = new GameManager(); 
    public static GameManager get(){return instance;} 

和波紋管,

private static GameManager _instance; 

    public static GameManager instance(){ 
    if(_instance == null) _instance = new GameManager(); 
    return _instance; 
    } 
+0

第一個在構造函數中實例化。 (新的GameManager()去構造函數)。但他們都是一樣的。 –

+0

如果你打算在鎖定等多線程上下文中使用這個單例,那麼這個單例的實例化應該小心。這就是爲什麼他們使用第二個實現來使用鎖,惰性(在C#中)等。 –

+1

在Java中,第二個版本並不保證該字段的單一實例化。如果該類可以併發訪問,則應該使用持有者類來執行此操作(或者同步更昂貴的方法以避免) – davidxxx

回答

3

主要有兩種不同的 以後每讀:

  1. 第一個例子是「渴望」,第二個例子是「懶惰」。具體來說,第一個將在單例類初始化後立即創建單例對象,而第二個將在第一次調用instance()時創建單例對象。

    一般來說,渴望初始化更簡單。然而,可能初始化可能取決於其他事情,並且初始化提供了一種延遲初始化直到它們發生的方式。

  2. 如果兩個或多個線程同時調用instance(),則第二個示例有一個隱患。具體來說,這兩個線程可能得到不同的GameManager對象。

    第一個示例中的類似問題是,如果應用程序中存在類初始化週期問題,則一個線程可能會看到null。這可能會導致一個線程看到null值。然而,類初始化的語義意味着在初始化類的初始化和調用方法之間存在一個之間的關係。因此,兩個線程都可以保證看到GameManager對象的正確初始狀態。


但需要注意的是,「雙鎖檢查」的例子只是針對Java 5及更高版本是正確的。


如果有多個線程共享GameManager例如,它是最有可能需要做其他事情讓應用程序(總是)表現正常。

3

它們都實現爲單例。唯一的區別是Singleton實例在第二個代碼塊上是Lazy Loaded。換句話說,在調用instance方法之前,它不會初始化GameManager。

你應該也可能是你的代碼改寫爲更簡單的東西,如:

public static GameManager instance = new GameManager(); 
+0

不正確的實現,因爲實例不是'final',類不是'final',並且構造函數不是'private'。 –

+0

自作聰明。這個問題被標記爲C#/ Java沒有最後的關鍵字。 –

+1

請原諒我沒有明確地表明我是從Java編程的背景來闡述的,對於這種暴行,我希望通過此處的確認來解決這個問題。 –

3

不同的是,第二個實現在多線程環境,因爲它可能會創建多個實例沒有好。這是當兩個線程檢查,如果情況== NULL在同一時間,並都得到真正的

注意選項#1也可以偷懶:

class GameManager { 
    private static GameManager instance = new GameManager(); 

    private GameManager() { 
     System.out.println("instance created"); 
    } 

    public static GameManager getInstance() { 
     return instance; 
    } 
} 

嘗試使用它,你會看到該實例僅在我們第一次調用getInstance()時創建。

+0

這兩個實現都不是正確的,應該提到。 –

-1

第一個實現比多處理器中的其他實現更安全。以下規則保證了爲什麼第一個實現是安全的。之前發生-的程序的任何其它動作(其他 比缺省寫入)

任何對象的默認初始化:

由於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並聲明_instancevolatile,下面的代碼:

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. 線程1,在點0在上面的代碼運行
  2. 線程1檢查_instance爲空,沒有人獲取鎖,所以它可以運行在點3
  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`,但注意實例沒有完全初始化(例如,沒有使用默認值初始化字段或調用靜態構造方法);
  4. thread2運行在0點,並檢查_instance不爲空,因此它會返回未完全初始化的_instance,並且該實例不是 值得信賴。

如果_instancevolatile,該volatile可以保證_instance完全初始化時,有人讀it.See的17.4.5 Happens-before Order,有一個規則:

寫的揮發性場(§8.3.1.4 )之前發生的是field.t

+0

實現不正確,因爲實例不是'final',類不是'final',構造函數也不是'private'。 「volatile」的描述不完整;其他寫入和讀取操作通過一個volatile變量建立_happens-before_。缺乏解釋爲什麼懶惰初始化不明智或不需要。沒有提到首選的枚舉方法。 –