2010-06-02 35 views
8

在Java中實現單例模式時,我想到了這個問題。儘管下面列出的例子不是我的真實代碼,但與原始代碼非常相似。如何在Java中同步靜態方法

public class ConnectionFactory{ 
    private static ConnectionFactory instance; 

    public static synchronized ConnectionFactory getInstance(){ 
     if(instance == null){ 
      instance = new ConnectionFactory(); 
     } 

     return instance; 
    } 

    private ConnectionFactory(){ 
     // private constructor implementation 
    } 
} 

因爲我不太肯定靜態同步方法的行爲,我從谷歌的一些建議 - 沒有(或儘可能少)在同一個類中的多個靜態同步方法。我猜在實現靜態同步方法時,使用屬於Class對象的鎖,以便多個靜態同步方法可能會降低系統的性能。

我對不對?或JVM使用其他機制來實現靜態同步方法?如果我必須在一個類中實現多個靜態同步方法,那麼最佳實踐是什麼?

謝謝大家!

親切的問候!

+2

是您真正的代碼也關心延遲初始化?因爲這通常會浪費代碼和開發人員的時間;在聲明中初始化字段是99%的時間做的正確的事情,並且不需要同步。 – 2010-06-02 13:34:01

回答

7

,最好的方法(這使你的代碼儘可能少的變化)是這樣做:

public class ConnectionFactory{ 
    private static ConnectionFactory instance = new ConnectionFactory(); 

    public static ConnectionFactory getInstance(){ 
     return instance; 
    } 

    private ConnectionFactory(){ 
    } 
} 

正如你所看到的,是在getInstance方法沒有真正的需要,因此您可以簡化的代碼:

public class ConnectionFactory{ 
    public static final ConnectionFactory INSTANCE = new ConnectionFactory(); 

    private ConnectionFactory(){ 
    } 
} 

UPD有關同步:

public class ConnectionFactory{ 
    private static final Object lock = new Object(); 

    public static void doSmth() { 
     synchronized (lock) { 

      ... 
     } 
    } 

    public static void doSmthElse() { 
     synchronized (lock) { 

      ... 
     } 
    } 
} 
:最好的方法是在一個鎖,其是不可見的外類,即同步

關於「爲什麼在this上同步是一個糟糕的主意」(如this one)有很多討論,我認爲在課堂上同步也是一樣的。

+0

如果唯一的構造函數拋出異常,它將如何創建'instance'? – unbeli 2010-06-02 13:23:04

+0

@unbeli:這是我的不好,糾正。但如果你是一個正確的解決方案,甚至不需要一個實例。 – Roman 2010-06-02 13:27:22

+0

@unbeli:通過使用私有靜態工廠方法或靜態初始化塊。不幸的是,所有關於Java內存模型和雙重檢查鎖定的討論都污染了互聯網上的數百個代碼示例,現在給新手留下的印象是,在getInstance()方法中懶惰地初始化單例實際上是個好主意,甚至是標準。 – 2010-06-02 13:31:42

2

是的,靜態方法在其類對象上同步。我不擔心這裏的表現,因爲這可能不會成爲你的表現熱點。做到簡單,優化何時何地需要。

2

靜態同步方法使用該類的鎖。在你的例子中,它將訪問ConnectionFactory類對象上的鎖。最佳做法是不要長時間持有鎖。無論你有多個同步方法本身都不是問題。

3

有幾種方法可以創建單例。

一個推薦的方法是使用一個枚舉(保證只創建一個實例):

public enum ConnectionFactory { 

    INSTANCE; 

} 

或類加載後,你可以靜態地創建它:

public class ConnectionFactory { 

    private static ConnectionFactory INSTANCE = new ConnectionFactory(); 

    private ConnectionFactory() {} 

    public static ConnectionFactory getInstance() { 
    return INSTANCE; 
    }  

} 

如果您需要懶洋洋地加載它,你可以使用這個成語(而非double checked locking anti-pattern

public class ConnectionFactory { 

    private static class ConnectionFactoryHolder { 
    private static ConnectionFactory INSTANCE = new ConnectionFactory(); 
    } 

    public static ConnectionFactory getInstance() { 
    return ConnectionFactoryHolder.INSTANCE; 
    } 

} 
0

有效的Java建議使用枚舉來創建單例。所以,你的代碼將是這個樣子:

public enum ConnectionFactory{ 
INSTANCE; 

// Other factory methods go here. 

} 

}