單例模式
單例模式
當你需樣以下幾個功能
- log紀錄
- 驅動程式
- 計分板
- 物件池
- 執行續池
- 提示視窗
這幾個只需要一個物件就可以處理的時候
不小心產生了兩個,不只會導致非預想的結果
還會有多餘的資源浪費
全域變數 VS 單例模式
你可以在程式定義一些全域變數
可以做到與單例模式一樣的效果
但有一些差別
- 控制物件的初始化時間,例如當你用不到這物件時,單例模式可以節省下記憶體
- 全域變數會因為執行順序影響到初始化
單例模式的範例
- 建構式是私有的
- 提供一個全域接觸點
public class Singleton{
private static Singleton Instance;
private Singleton(){}
public static Singleton getInstance(){
if(Instance == null)
Instance = new Singleton();
return Instance;
}
}
單例模式VS多執行序
單例模式在多執序下
可能會造成非預期的執行順序
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| thread1 | 1 | - | 2 | - | 4 | 5 | - | - |
| thread2 | - | 1 | - | 2 | - | - | 4 | 5 |
本來預想是只能初始化一個實例,但如果初始化兩次
就會造成許多意外錯誤
優化執行序的問題
1.lazy與eager 建立方式
lazy是當應用程式有需求時,才會去建立物件
public static Singleton getInstance(){
if(Instance == null)
Instance = new Singleton();
return Instance;
}
eager是直接在應用程式上面,實例化單例模式
當你的運行成本還在可控範圍內可以使用
private static Singleton Instance = new Singleton();
2.使用同步
可以使多個執行序比需等待這個區塊的程式碼
不過前提是這個區塊的效能就會低下,每個執行序必須等待上一個執行完
C#範例:
使用lock,一次只有一個線程可以進入此區塊
private static readonly object padlock = new object();
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
java範例:
加入synchronized關鍵字
public static synchronized Singleton getInstance(){
if(Instance == null)
Instance = new Singleton();
return Instance;
}
3.雙重檢查鎖
使用雙重檢查鎖,可以在第一次還未初始化時才同步
因此可以增加執行效率
C#範例:
在第一層判斷實例化的地方加入lock
如此可以在第一次還未初始化時才同步
private static readonly object padlock = new object();
public static Singleton Instance
{
get
{
if (instance == null)
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
java範例:
加入volatile關鍵字,可以對使用此關鍵字的變數使用synchronized
private volatile static Singleton Instance = new Singleton();
public static synchronized Singleton getInstance(){
if(Instance == null){
synchronized(Singleton.class){
if(Instance == null)
Instance = new Singleton();
}
}
return Instance;
}
參考資料:
C# 單例模式