單例是最廣泛使用的創建型設計模式之一,用於限制應用程序創建的對象。如果您在多線程環境中使用它,那麼單例類的線程安全性就非常重要。在現實世界的應用程序中,資源如數據庫連接或企業信息系統(EIS)是有限的,應該明智地使用以避免任何資源不足。為了實現這一點,我們可以實現一個單例設計模式。我們可以為資源創建一個包裝器類,並在運行時將創建的對象數量限制為一個。
Java中的線程安全單例
一般而言,我們按照以下步驟創建單例類:
使用上述步驟,我已經創建了一個看起來如下的單例類。 ASingleton.java
package com.journaldev.designpatterns;
public class ASingleton {
private static ASingleton instance = null;
private ASingleton() {
}
public static ASingleton getInstance() {
if (instance == null) {
instance = new ASingleton();
}
return instance;
}
}
在上面的代碼中,getInstance()方法不是線程安全的。多個線程可以同時訪問它。對於前幾個線程,當實例變量未初始化時,多個線程可以進入if循環並創建多個實例。這將破壞我們的單例實現。
如何在單例類中實現線程安全?
有三種方法可以實現線程安全。
- 在類加載時創建實例變量。
優點:
- 無需同步即可實現線程安全
- 易於實現
缺點:
- 提前創建可能在應用中未使用的資源。
- 客戶端應用程序無法傳遞任何參數,因此我們無法重複使用它。例如,為數據庫連接創建通用單例類,客戶端應用程序提供數據庫服務器屬性。
- 同步getInstance()方法。
優點:
- 線程安全得到保證。
- 客戶端應用程序可以傳遞參數
- 實現了延遲初始化
缺點:
- 由於鎖定開銷,性能較慢。
- 一旦實例變量初始化,就不需要的不必要同步。
- 在if循環內使用同步塊和volatile變量
優點:
- 線程安全得到保證
- 客戶端應用程序可以傳遞參數
- 實現了延遲初始化
- 僅在變量為null時,同步開銷才最小且僅適用於前幾個線程。
缺點:
- 額外的if條件
綜觀實現線程安全的三種方法,我認為第三種是最佳選擇。在這種情況下,修改後的類將如下所示:
package com.journaldev.designpatterns;
public class ASingleton {
private static volatile ASingleton instance;
private static Object mutex = new Object();
private ASingleton() {
}
public static ASingleton getInstance() {
ASingleton result = instance;
if (result == null) {
synchronized (mutex) {
result = instance;
if (result == null)
instance = result = new ASingleton();
}
}
return result;
}
}
局部變量result
似乎是不必要的。但是,它存在是為了改善我們代碼的性能。在實例已經初始化的情況下(大多數情況下),由於“return result;”而不是“return instance;”,volatile字段只被訪問一次。這可以提高方法整體性能多達25%。如果您認為有更好的方法可以實現這一點,或者如果上述實現中線程安全受到損害,請發表評論並與我們分享。
獎金提示
String不太適合與synchronized關鍵字一起使用。這是因為它們存儲在一個字符串池中,我們不希望鎖定一個可能被另一段代碼使用的字符串。因此,我使用一個Object變量。了解更多關於Java中的同步和線程安全的信息。
您可以從我們的GitHub存儲庫中查看更多Java示例。
Source:
https://www.digitalocean.com/community/tutorials/thread-safety-in-java-singleton-classes