保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
要想控制一個類只被創建一個實例,那么首要的問題就是把創建實例的權限收回來,讓類自己來負責創建自己類的實例,然后由這個類來提供外部可以訪問這個類實例的方法,這就是單例模式的實現方法。
單例模式的結構和實現

Singleton:負責創建Singleton類自己的唯一實例,并提供一個getInstance的方法,來外部來訪問這個類的唯一實例。
- 私有化構造方法
- 提供靜態的獲取實例的方法
- 定義存儲實例的屬性,因為要在靜態方法中使用,因此要加上static修飾
- 實現控制實例的創建
示例代碼
懶漢模式
所謂懶漢模式,既然懶,那么在創建對象實例時就不要著急,在馬上要使用對象實例時才會創建,在裝載對象時不會創建對象實例。
package com.liuhao.designpattern.singleton;
/**
* 單例模式懶漢模式
*
* @author liuhao
*
* 2015年7月17日
*/
public class SingletonLazy {
// 4. 定義一個變量來存儲創建好的類實例
// 5. 因為要在靜態方法中使用,因此要加上static修飾
private static SingletonLazy instance = null;
// 1. 構造方法私有化,好在內部控制創建實例的數目
private SingletonLazy() {
}
// 2. 定義一個方法為客戶端提供類實例
// 3. 這個方法需要定義成類方法,也就是
public static SingletonLazy getInstance() {
// 6. 判斷存儲實例的變量是否有值
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
private String singletonData;// 單例可以有自己的屬性
/**
* 獲取屬性的值
*
* @return 屬性的值
*/
public String getSingletonData() {
return singletonData;
}
/**
* 單例可以有自己的操作方法
*/
public void singletonOperation() {
}
}
餓漢模式
所謂餓漢模式,就是在創建對象實例時比較急,在裝載類的時候就會創建對象實例。
package com.liuhao.designpattern.singleton;
/**
* 單例模式餓漢模式
*
* @author liuhao
*
* 2015年7月17日
*/
public class SingletonHungry {
// 4. 直裝載類的時候就會創建對象實例,只創建一次
private static SingletonHungry instance = new SingletonHungry();
// 1. 構造方法私有化,好在內部控制創建實例的數目
private SingletonHungry() {
}
// 2. 定義一個方法為客戶端提供類實例
// 3. 這個方法需要定義成類方法,也就是
public static SingletonHungry getInstance() {
// 5. 直接使用以及創建好的實例
return instance;
}
private String singletonData;// 單例可以有自己的屬性
/**
* 獲取屬性的值
*
* @return 屬性的值
*/
public String getSingletonData() {
return singletonData;
}
/**
* 單例可以有自己的操作方法
*/
public void singletonOperation() {
}
}
其他
單例模式的范圍
目前Java實現的單例是一個虛擬機范圍的,因為裝載類的功能是虛擬機的,所以一個虛擬機在通過自己的ClassLoader裝載餓漢模式實現單例時就會創建一個類的實例。
因此如果一個虛擬機里面有很多個ClassLoader,而這些ClassLoader都裝載某個類的話,就會產生多個實例。
單例模式的調用順序
- 懶漢模式

- 餓漢模式

體現的一些思想
延遲加載
懶漢模式中,一開始沒有加載所需的資源或者數據,一直等到馬上就要使用了才加載,即所謂的“延遲加載”。緩存
當某些資源或數據被頻繁的使用,而且它們是存儲在系統外部的(如數據庫、硬盤),那么每次操作都要從數據庫獲取,速度會很慢,操作性能問題。
一個簡單的方法就是把這些數據緩存到內存中,每次操作的時候先到內存里找,若有則直接使用;若果沒有再獲取它并設置到緩存中。
緩存是一種典型的空間換取時間的方案。
線程安全
-
不加同步的懶漢模式是線程不安全的。比如,有線程A、B同時調用getInstance方法,那就可能導致并發問題,如圖。
懶漢模式線程沖突 -
如何實現懶漢模式的線程安全
- 加上關鍵字
synchronized
,如下
public static synchronized SingletonLazy getInstance() {}
- 雙重檢查加鎖。不是在每次進入getInstance時都需要同步,而是先不同步,進入方法后,先檢查實例是否存在。如果不存在才進入下面的同步塊,這是第一重檢查。進入同步塊后,再次檢查實例是否存在,如果不存在,就在同步的情況下創建一個實例,這是第二重檢查。
雙重檢查加鎖機制的實現需要使用volatile
關鍵字,被它修飾的變量的值將不會被本地線程緩存,所有對該變量的讀寫都是直接操作共享內存,從而確保多個線程能夠正確的處理該變量。
具體實現:
public static SingletonVolatile getInstance() { // 檢查實例是否存在,不存在則進入到同步塊 if (instance == null) { // 同步塊,線程安全地創建實例 synchronized (SingletonVolatile.class) { // 再次檢查實例是否存在,不存在則真正的創建實例 if (instance == null) { instance = new SingletonVolatile(); } } } return instance; }
這種實現方式既可以實現線程安全,同時也不會對性能造成太大的影響。
- 加上關鍵字
餓漢模式是線程安全的。
如果覺得有用,歡迎關注我的微信,有問題可以直接交流:
