--- tags: Creational Patterns --- # 單例模式 Singleton ## 快速閱讀 確保一個class只會有一個實體物件。 在Singleton Pattern 只出現一個 Singleton 參與者。 Singleton 的參與者具有 **static** 方法可取得唯一的物件個體。這個方法永遠都會傳回同一個物件個體。 為什麼要使用這個?因為物件一個以上的話,可能會造成物件之間的影響或是意料外的bug。 防止多執行序需要加**synchronized**關鍵字。 ## 程式要點 1. 不能讓其他人new出來,所以必須單獨寫一個java檔案且實體的變數需要是private。 2. 多執行緒可能無法確保這個物件是唯一,需要加synchronized。 ### 如何寫 我認為可以螢幕介面或是下方的class當作例子 **範例程式 | Singleton** ```java= public class Singleton { // 利用一個靜態變數記綠 Singleton 的實體 private static Singleton sInstance; // 可以宣告其他需要的成員變數 // 建構式宣告為 private, 這樣只有在Singleton 類別內才能使用 private Singleton(){} // 公開的靜態方法, 其他人要取得物件只能使用此方法 public static Singleton getInstance(){ // 不為 null, 表示之前曾建立過, 不用再 new 一次 if(sInstance == null){ // 需要時才建立物件, 稱為 lazy instantiaze sInstance = new Singleton(); } return sInstance; } } ``` > [name=閔致] 上面所看到的範例是參考資料中的Lazy Initialization,當呼叫getInstance()後,發現sInstance為null,於是乎在當下建立一個,下面會提到另一個是Eager Initialization的範例,則是在Class建立時就會直接建立一個sInstance。 **範例程式 | Eager Initialization** ```java= public class Singleton { // 利用一個靜態變數記綠 Singleton 的實體 private static Singleton sInstance = new Singleton(); // 可以宣告其他需要的成員變數 // 建構式宣告為 private, 這樣只有在Singleton 類別內才能使用 private Singleton(){} // 公開的靜態方法, 其他人要取得物件只能使用此方法 public static Singleton getInstance(){ //回傳前面建立的實體 return sInstance; } } ``` 兩種Singleton主要的差異就是在Instance被建立的時間點不同,下面整理一份表格用於參考兩者之間的差異。 | | Lazy | Eager | | ------------------ | ---------- | ---------- | | Instance建立時間點 | 使用者用到 | 系統初始化 | | 優點 | 節省資源 | 降低程式碼的複雜度,單/多執行緒適用 | | 缺點 | 可能出現 Instance 重複建立的狀況 | 較浪費資源(系統開始時就全部建立,無論是否用到) | > [name=祐民] ## synchronized 使用第一個範例程式|synchronized synchronized關鍵字是用來宣告一個method或是一段程式碼是為同步,意思就是在同步方法/區塊/成員變數中一次只允許一條執行緒存取被鎖定的物件。 **範例程式 | synchronized** ```java= public class Singleton { private static Singleton sInstance = new Singleton(); private Singleton(){} //加入synchronized public static synchronized Singleton getInstance(){ if(sInstance == null){ sInstance = new Singleton(); } return sInstance; } } ``` > [name=毓昇]我覺得上面怪怪的,我改成下面 ```java= public class Singleton { private static Singleton sInstance; private Singleton{ //這裡跑了很多code,建立物件需要花費很多資源 } private Singleton(){} //加入synchronized public static synchronized Singleton getInstance(){ if(sInstance == null){ sInstance = new Singleton(); } return sInstance; } } ``` 假如class規模比較小時,可以使用第二個範例程式 | Eager Initialization。 如果在建立物件時需要比較大的 loading 時,此方法也不適用,因此還可以改為使用「[雙重檢查上鎖](https://zh.wikipedia.org/wiki/%E5%8F%8C%E9%87%8D%E6%A3%80%E6%9F%A5%E9%94%81%E5%AE%9A%E6%A8%A1%E5%BC%8F)」(double-checked locking) 的方式,簡單說就是因為 synchronized 包的程式碼變少,效能變好。 **範例程式 | double-checked locking** ```java= public class Singleton { // 利用 volatile, 可以保證此變數的值是一致的 private volatile static Singleton sInstance; private Singleton(){} public static Singleton getInstance(){ // 只有第一次建立物件才會完整執行此段程式 if(sInstance == null){ synchronized (Singleton.class){ // 進入同步區後再檢查一次, // 還是 null 才建立實體 if(sInstance == null){ sInstance = new Singleton(); } } } return sInstance; } } ``` > [name=閔致]
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up