1. 程式人生 > 其它 >單例模式的理解

單例模式的理解

1.單例模式

概念:

​ 為了節省記憶體資源、保證資料內容的一致性,對某些類要求只能建立一個例項,這就是所謂的單例模式。單例模式是設計模式中最簡單的模式之一。通常,普通類的建構函式是公有的,外部類可以通過“new 建構函式()”來生成多個例項。但是,如果將類的建構函式設為私有的,外部類就無法呼叫該建構函式,也就無法生成多個例項。這時該類自身必須定義一個靜態私有例項,並向外提供一個靜態的公有函式用於建立或獲取該靜態私有例項。

適用場景:

1.1 單例的實現

一,基本的實現方式:

  1. 靜態化例項物件
  2. 私有化構造方法,禁止通過構造方法建立例項
  3. 提供一個公共的靜態方法,用來返回唯一例項

二,分類:

實際開發過程中要考慮的點:

以上幾種分類需要關注三個方面進行選擇使用:
1.是否執行緒安全;
2.最好是懶載入,避免資源的浪費;
3.呼叫效率高一些,也就是涉及到同步的問題;

方式一:餓漢式示例

解析:

​ 餓漢式的特點是static變數,也就是該類的物件,在類載入時就會初始化生成該類物件。由於虛擬機器在類載入初始化時候保證只加載一次該類,形成天然安全的執行緒安全環境,所以肯定不會發生併發訪問的問題,因此不必加synchronized關鍵字來進行同步。以上兩個特點會出現以下問題:

  1. 因為不需要同步,所以呼叫效率高;
  2. 因為類載入時直接生成了該類物件,如果沒有呼叫該類,會造成資源的浪費;
package com.ethan.singleton;
/**
 * 1.餓漢式單例模式
 * @author ethan
 *
 */
public class SingletonDemo1 {
	/*
	 * 	1.類初始化載入時就直接載入生成該類物件
	 * 	2.執行緒安全,載入類時候天然的執行緒安全模式
	 * 	3.不是延時載入物件,可能會造成資源的浪費
	 */
	
	private static SingletonDemo1 instance = new SingletonDemo1();
	private SingletonDemo1() {
		
	}
	// 方法沒有同步,呼叫效率高。
	public static SingletonDemo1 getInstance() {
		return instance;
	}
	
}

方式二:懶漢式示例

解析:

​ 懶漢式的特點是延時載入生成該類的物件,必須呼叫getInstance()方法才會生成該類物件,真正用時候才會載入;但是可能會發生併發訪問的問題,因此需要加synchronized關鍵字來進行同步。以上兩個特點會出現以下問題:

  1. 因為需要同步,所以呼叫效率低;
  2. 用的時候才載入,不會造成資源的浪費;
package com.ethan.singleton;
/**
 * 2.懶漢式單例模式
 * @author ethan
 *
 */
public class SingletonDemo2 {
	//類初始化載入時,不初始化生成物件,延時載入生成該類物件,即真正使用物件時候在建立
	private static volatile SingletonDemo2 instance = null;
	private SingletonDemo2() {//私有化構造器
		
	}
	// 方式一:方法需要同步,導致呼叫效率低!
	public static synchronized SingletonDemo2 getinstance() {
		if(instance == null) {
			instance = new SingletonDemo2(); 
		}
		return instance;
	}
}

方式三:雙重檢測鎖式

​ 懶漢式因為同步,雖然執行緒安全,但是呼叫效率會低一些,有以下改進,利用雙重檢測物件是否為空,縮小同步程式碼塊的範圍,提高效率。

package com.ethan.singleton;
/**
 * 3.雙重檢測鎖式
 * @author ethan
 *
 */
public class SingletonDemo2 {
	//類初始化載入時,不初始化生成物件,延時載入生成該類物件,即真正使用物件時候在建立
	private static volatile SingletonDemo2 instance = null;
	private SingletonDemo2() {//私有化構造器
		
	}
	//雙重檢測,相比第二種方式,這種方式效率更好
    public static SingletonDemo2 getinstance() {
        if(instance == null) {
            synchronized (SingletonDemo2.class){
                if (instance == null){
                    instance = new SingletonDemo2();
                }
            }  
        }
        return instance;
    }
}




方式四:靜態內部類式示例

解析:

​ 外部類SingletonDemo4沒有static變數,類載入時不會像餓漢式那樣立即載入類物件;只有在真正呼叫getInstance()之後才會載入內部類,從而生成內部類的靜態變數,也就是外部類的物件,因為載入類時候執行緒安全,instance是static final 型別,保證了記憶體中只有一個例項存在,而且只能被賦值一次,保證執行緒的安全性;兼備延時載入和併發安全,高效呼叫的優勢;

理解靜態內部類的生成時機。

package com.ethan.singleton;
/**
 * 4.靜態內部類實現單例模式
 * @author ethan
 *
 */
public class SingletonDemo4 {
	private static class SingletonDemo4Instance {
		//final加不加都行
		private static final SingletonDemo4 instance = new SingletonDemo4();
	}
	private SingletonDemo4(){
		
	}
	public static SingletonDemo4 getInstance() {
		return SingletonDemo4Instance.instance;
	}
}	

方式五:列舉式示例

解析:

​ 列舉類的物件天然是唯一的,那麼建立一個列舉類,並且只有一個列舉值(列舉物件),那麼這個列舉類就只能有一個物件,符合單例模式。避免了反射去生成其物件和序列化的漏洞,沒有延時載入的效果。

package com.ethan.singleton;

public enum SingletonDemo5 {
	//這個列舉元素,本身就是單例
	INSTANCE;
	
	//新增自己需要的操作
	public void singletonOperation() {
		System.out.println("舉例列舉單例的操作!!!");
	}
}


2. 總結:

選用標準

  1. 需要佔用資源少,不需要延時載入 --->列舉好於餓漢式
  2. 佔用資源大,需要延時載入 --->靜態內部類式好於懶漢式

2.1 回顧的知識點

static的理解,final的理解,靜態內部類的理解,列舉的理解,多執行緒的同步;