1. 全局變量的缺點:
必須在程序一開始就創建好對象,如果程序在這次的執行過程中又一直沒用到它,就非常耗費資源。
2. 經典的單例模式實現:
public class Singleton { //用一個靜態變量來記錄Singleton類的唯一實例 private static Singleton uniqueInstance; private Singleton() {} //注意這個方法也是靜態的 public static Singleton getInstance() { if(uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
單例常被用來管理共享的資源,例如數據庫連接、線程池、緩存、注冊表。
單例模式確保一個類只有一個實例,並提供一個全局訪問點。
這個模式的問題:在多線程時,並不能保證這個類只被實例化一次。
3. 處理多線程:
通過增加synchronized關鍵字到getInstance()方法中,迫使每個線程在進入方法之前,要先等別的線程離開該方法。也就是說,不會有兩個線程可以同時進入這個方法。
這種方法存在的問題:只有第一次執行此方法時,才真正需要同步。換句話說,一旦設置好uniqueInstance變量,就不再需要同步這個方法了。之后每次調用這個方法,同步都是一種浪費。
4.改善多線程
4.1 如果getInstance()的性能對應用程序不是很關鍵,就不用優化了
4.2 使用急切創建實例,而不用延遲實例化的做法
public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return uniqueInstance; } }
標紅的語句在靜態初始化器(static initializer)中創建單例,這保證了線程安全。
利用這個做法,JVM在加載這個類時馬上創建此唯一的單件實例。JVM保證任何線程訪問uniqueInstance靜態變量之前,一定先創建些實例。
4.3 用“雙重檢查加鎖”,在getInstance()中減少使用同步
首先檢查實例是否已經創建,如果尚未創建,才進行同步。這樣一來,只有第一次會同步,這正是我們想要的。
public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() {} public static Singleton getInstance() { if(uniqueInstance == null) {//(1)
//只有第一次才徹底執行這里的代碼 synchronized() { //再檢查一次 if(uniqueInstance == null) uniqueInstance = new Singleton(); } } return uniqueInstance; } }
在最開始如果有1、2、3個線程走到了(1)處,假設1進入了同步塊,2、3等待。1實例化后,2進入同步塊,發現uniqueInstance已經不為空,跳出同步塊。接着3進入,又跳出同步塊。
volatile關鍵字確保:當uniqueInstance變量被初始化成Singleton實例時,多個線程正確地uniqueInstance變量。如果性能是你關心的重點,那么這個做法可以幫你大大地減少getInstance()的時間耗費。