2012-04-27 106 views
8

我有一個類:從靜態方法返回一個引用線程安全嗎?

class PrintStringDataBuilder 
{ 
    PrintStringDataBuilder() { } 
    public static GetInstance() 
    { 
     return new PrintStringDataBuilder(); 
    } 

    //other class methods and fields, properties 
} 

從客戶端訪問的代碼爲:

PrintStringDataBuilder instance = PrintStringDataBuilder.GetInstance(); 

是上面的調用線程安全的?

編輯:只是試圖避免寫作 PrintStringDataBuilder builder = new PrintStringDataBuilder();在asp.net mvc web應用程序中多次。 PrintStringDataBuilder類中沒有其他靜態方法,靜態字段或靜態屬性。

+0

這真的取決於'新的PrintStringDataBuilder()'的作用。你想讓它變成單身?如果是這樣,那不是這樣做的。如果不是,那麼爲什麼只要調用構造函數就可以有一個靜態的'GetInstance()'方法。 – cadrell0 2012-04-27 17:49:55

+0

你有一個私人構造函數'PrintStringDataBuilder'你是如何初始化其他字段? – 2012-04-27 17:50:41

+0

爲什麼選擇倒票?我認爲這是個好問題 – n8wrl 2012-04-27 17:52:16

回答

11

是嗎?如果不知道該類的構造函數的內部,可以說調用GetInstance()是線程安全的。儘管如此,該實例上的任何方法都不能保證是線程安全的,特別是因爲您沒有提供任何這些方法。

這被簡單地稱爲工廠模式。

編輯:如果你試圖返回一個單,你能做到像這樣:

.NET 4+

private static Lazy<PrintStringDataBuilder> _instance = new Lazy<PrintStringDataBuilder>(() => 
    { 
     return new PrintStringDataBuilder(); 
    }); 

public static PrintStringDataBuilder GetInstance() 
{ 
    return _instance.Value; 
} 

.NET 3.5及以下

private static PrintStringDataBuilder _instance = null; 
private static object _lockObject = new object(); 

public static PrintStringDataBuilder GetInstance() 
{ 
    if(_instance == null) 
    { 
     lock(_lockObject) 
     { 
       if(_instance == null) 
       _instance = new PrintStringDataBuilder(); 
     } 
    } 

    return _instance; 
} 
+0

對不起,忘了寫構造函數。我更新了代碼。 – mxasim 2012-04-27 17:55:09

+0

是的,構造函數是線程安全的(它什麼都不做)。你想要返回一個單身人士嗎? – Tejs 2012-04-27 17:55:40

+0

不,試圖避免編寫PrintStringDataBuilder builder = new PrintStringDataBuilder();多次 – mxasim 2012-04-27 17:57:07

5

通過'threadsafe'是否擔心多個調用您的靜態方法的線程將獲得相同的PrintStringDataBuilder?答案是NO,並且調用是線程安全的。

話雖如此,沒有人可以從你給出的小片段中知道其他人是否是其他的類,或者它的構造函數。類實例不是線程安全的有很多原因。如果他們引用靜態屬性而不鎖定是一個例子。

3

輸入方法始終是線程安全的。訪問共享數據可能不會。所以這段代碼是線程安全的,因爲沒有共享數據。

如果您的意圖是爲所有線程提供一個PrintStringDataBuilder的實例,那麼爲此您的代碼將無法工作。你需要適當的單身人士。在.NET 4中的代碼可以非常緊湊:

private static Lazy<PrintStringDataBuilder> instance = new Lazy<PrintStringDataBuilder>(); 

public static PrintStringDataBuilder Instance 
{ 
    get { return instance.Value; } 
} 

這將保證在每個線程PrintStringDataBuilder.Instance將指向PrintStringDataBuilder對象的相同,只有一個實例將在一個懶惰的方式產生,即只有當它是第一次使用,並儘快。

1

@Tejs,

其實,在.NET你不需要使用雙檢鎖機構 - 周圍有更好的方式。但是如果你選擇這樣做,那麼你的雙重檢查鎖的實現是不正確的,並且不是真正的線程安全的。編譯器可以優化掉_instance = new PrintStringDataBuilder();的初始化 - 有3次可能的修改,讓您真正例如線程安全:

  1. 初始化靜態成員在線 - 絕對最簡單的!
private static PrintStringDataBuilder _instance = new PrintStringDataBuilder; 
    public static PrintStringDataBuilder GetInstance() 
    { 
     return _instance; 
    } 

2。使用'volatile'關鍵字來確保PrintStringDataBuilder的初始化沒有被JIT優化。


private static volatile PrintStringDataBuilder _instance = null; 
private static object _lockObject = new object(); 

public static PrintStringDataBuilder GetInstance() 
{ 
    if(_instance == null) 
    { 
     lock(_lockObject) 
     { 
       if(_instance == null) 
       { 
       _instance = new PrintStringDataBuilder(); 
       } 
     } 
    } 

    return _instance; 
}

3。使用Interlocked.Exchange與雙重檢查鎖:


private static PrintStringDataBuilder _instance = null; 
private static object _lockObject = new object(); 

public static PrintStringDataBuilder GetInstance() 
{ 
    if(_instance == null) 
    { 
     lock(_lockObject) 
     { 
       if(_instance == null) 
       { 
       var temp = new PrintStringDataBuilder(); 
       Interlocked.Exchange(ref _instance, temp); 
       } 
     } 
    } 

    return _instance; 
}

希望這會有所幫助。

相關問題