如果餓了就喫,困了就睡,渴了就喝,人生就太無趣了
1.定義
1.1 概念
確保一個類只有一個實例,並提供一個全局訪問點。簡單來說:將構造器私有化,並在靜態方法中初始化該類的靜態變量。
1.2 類圖
如圖1,定義一個該類的私有靜態變量uniqueInstance
,並將構造器定義爲private
(在圖中沒有顯示),通過靜態方法getInstance()
爲uniqueInstance
初始化並返回該實例。
2.單例模式各種類型。
2.1 經典類型
在使用時會被創建,但是在併發模式中會出現問題。
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){}
public static Singleton getInstance(){
if(uniqueInstance==null){
uniqueInstance=new Singleton();
}
return uniqueInstance;
}
}
2.2 同步鎖類型
將getInstance
變成同步方法。在方法聲明中加入 synchronized
關鍵字。
synchronized:讓每個線程進入該關鍵字修飾的方法前,要先等待其他線程離開後才能訪問。
問題:這樣每次訪問getInstance()
都會需要同步,但實際上只要uniqueInstance
被設置好後就不需要同步,只是第一次訪問需要同步。如果程序需要大量訪問getInstance()
方法,就會大大降低性能。
public class Singleton {
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
2.3 急切類型
將uniqueInstance
變量初始化交給靜態初始化器。JVM
將保證線程安全,因爲JVM
在加載這個類時會馬上創建一個唯一的單間實例,JVM
保證任何線程訪問uniqueInstance
前,一定會先創建此實例。
public class Singleton {
private volatile static Singleton uniqueInstance=new Singleton();
private Singleton() {}
public static Singleton getInstance(){
return uniqueInstance;
}
}
2.4 雙重檢查加鎖類型
使用雙重檢查加鎖(double-checked locking),首先檢查實例是否已經創建了,如果尚未創建,再進行同步。這樣就只有第一次纔會同步。
volatile:
- 類型修飾符。
- 保證了不同線程對這個變量進行操作時的可見性。例如果對變量值進行改變,對其他線程是立即可見。(實現可見性)
- 禁止進行指令重排序(實現有序性)
- 保證對單次讀/寫的原子性。i++ 這種操作不能保證原子性。
這樣就確保了uniqueInstance
被初始化時,多個線程能正確處理uniqueInstance
變量。
getInstance()
方法中的第一個if
保證在uniqueInstance
初始化後,避免同步。getInstance()
方法中的第二個if
保證在線程a,b同時進入後,能夠有序執行同步鎖中的內容。沒有第二個if
也是不能保證單例的。
public class Singleton {
private volatile static Singleton uniqueInstance;
private Singleton() {
}
public static Singleton getInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}
2.5 其他類型
沒有搞定,也就不敢亂寫。