單例模式的理解
1.單例模式
概念:
為了節省記憶體資源、保證資料內容的一致性,對某些類要求只能建立一個例項,這就是所謂的單例模式。單例模式是設計模式中最簡單的模式之一。通常,普通類的建構函式是公有的,外部類可以通過“new 建構函式()”來生成多個例項。但是,如果將類的建構函式設為私有的,外部類就無法呼叫該建構函式,也就無法生成多個例項。這時該類自身必須定義一個靜態私有例項,並向外提供一個靜態的公有函式用於建立或獲取該靜態私有例項。
適用場景:
1.1 單例的實現
一,基本的實現方式:
- 靜態化例項物件
- 私有化構造方法,禁止通過構造方法建立例項
- 提供一個公共的靜態方法,用來返回唯一例項
二,分類:
實際開發過程中要考慮的點:
以上幾種分類需要關注三個方面進行選擇使用:
1.是否執行緒安全;
2.最好是懶載入,避免資源的浪費;
3.呼叫效率高一些,也就是涉及到同步的問題;
方式一:餓漢式示例
解析:
餓漢式的特點是static變數,也就是該類的物件,在類載入時就會初始化生成該類物件。由於虛擬機器在類載入初始化時候保證只加載一次該類,形成天然安全的執行緒安全環境,所以肯定不會發生併發訪問的問題,因此不必加synchronized關鍵字來進行同步。以上兩個特點會出現以下問題:
- 因為不需要同步,所以呼叫效率高;
- 因為類載入時直接生成了該類物件,如果沒有呼叫該類,會造成資源的浪費;
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關鍵字來進行同步。以上兩個特點會出現以下問題:
- 因為需要同步,所以呼叫效率低;
- 用的時候才載入,不會造成資源的浪費;
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. 總結:
選用標準
- 需要佔用資源少,不需要延時載入 --->列舉好於餓漢式
- 佔用資源大,需要延時載入 --->靜態內部類式好於懶漢式
2.1 回顧的知識點
static的理解,final的理解,靜態內部類的理解,列舉的理解,多執行緒的同步;