寫在前面
單例模式定義: 一個類 只能創建一個實例。
使用一個對象來做就不用實例化多個對象!這就能減少我們空間和內存的開銷~
我們使用靜態類.doSomething()和使用單例對象調用方法的效果是一樣的啊。
沒錯,效果就是一樣的。使用靜態類.doSomething()體現的是基於對象,而使用單例設計模式體現的是面向對象。
編寫單例模式的代碼其實很簡單,就分了三步:
將構造函數私有化
在類的內部創建實例
提供獲取唯一實例的方法
常用的單例模式
- 餓漢式
public class Liuge36 {
// 1.構造函數私有化
private Liuge36(){
}
// 2.在類的內部創建實例
private static Liuge36 liuge36 = new Liuge36();
// 3.提供獲取實例的唯一方法
public static Liuge36 getInstance(){
return liuge36;
}
}
一上來就創建對象了,如果該實例從始至終都沒被使用過,則會造成內存浪費。
- 簡單懶漢式 (在方法上加鎖)
既然說一上來就創建對象,如果沒有用過會造成內存浪費:
那麼我們就設計用到的時候再創建對象!
public class Liuge36 {
// 1.將構造函數私有化
private Liuge36(){
}
// 2.先不創建對象,等用到的時候 ,再開始創建
private static Liuge36 liuge36 = null;
/**
* 3.調用獲取實例的方法,說明要用到實例對象了
* synchronized 加鎖之後才能保證在多線程下代碼可用,不加鎖僅僅適用於單線程的情況
*/
public static synchronized Liuge36 getInstance(){
// 先判斷對象是否爲空,如果爲空,先創建再返回出去
if (liuge36 == null) {
liuge36 = new Liuge36();
}
return liuge36;
}
}
- DCL雙重檢測加鎖(進階懶漢式)
上面那種直接在方法上加鎖的方式其實不夠好,因爲在方法上加了內置鎖在多線程環境下性能會比較低下,所以我們可以將鎖的範圍縮小。
public class Liuge36 {
// 1.將構造函數私有化
private Liuge36(){
}
/**
* 利用靜態變量liuge36來記錄Liuge36的唯一實例
*
* volatile 關鍵字確保:當變量liuge36 被初始化成 Liuge36實例時,
* 多個線程正確地處理liuge36變量
*
* volatile有內存屏障的功能!
*/
private static volatile Liuge36 liuge36 = null;
// 提供獲取唯一實例的方法
public static Liuge36 getInstance(){
if (liuge36 == null){
// 同步代碼塊 ,將鎖的範圍縮小,提高性能
synchronized (Liuge36.class){
// 再次判斷對象是否創建過
if (liuge36 == null){
liuge36 = new Liuge36();
}
}
}
return liuge36;
}
}
- 靜態內部類實現懶漢式
public class Liuge36 {
// 1.將構造函數私有化
private Liuge36(){
}
/**
* 使用靜態內部類的方式實現懶加載
* 初始化靜態數據時,Java提供了的線程安全性保證。(所以不需要任何的同步)
*/
private static class LazyHolder{
// 創建單例對象
private static final Liuge36 instance = new Liuge36();
}
// 提供獲取實例的唯一方法
public static Liuge36 getInstance(){
return LazyHolder.instance;
}
}
- 枚舉方式
Joshua Bloch說“單元素的枚舉類型已經成爲實現Singleton的最佳方法
public class Liuge36 {
// 1.將構造函數私有化
private Liuge36(){
}
// 2 定義一個靜態枚舉類
static enum SingletonEnum{
INSTANCE;
private Liuge36 liuge36;
// 私有化枚舉的構造函數
private SingletonEnum(){
liuge36 = new Liuge36();
}
public Liuge36 getLiuge36(){
return liuge36;
}
}
// 3. 提供獲取實例的唯一方法
public static Liuge36 getInstance(){
return SingletonEnum.INSTANCE.getLiuge36();
}
public static void main(String [] args){
System.out.println(Liuge36.getInstance());
System.out.println(Liuge36.getInstance());
System.out.println(Liuge36.getInstance()==Liuge36.getInstance());
}
}
總的來說單例模式寫法有5種:
餓漢式
簡單懶漢式(在方法加鎖)
DCL雙重檢測加鎖(進階懶漢式)
靜態內部類實現懶漢式(最推薦寫法)
枚舉方式(最安全、簡潔寫法)
參考:java3y & https://www.jianshu.com/p/d35f244f3770