有時候某些對象咱們只須要一個,如:線程池、緩存、對話框等等,對於這類對象咱們只能有一個實例,若是我web
們製造出多個實例,就會致使不少問題產生。設計模式
可是咱們怎樣才能保證一個類只有一個實例而且可以便於訪問?這裏咱們想到了全局變量,全局變量確實是能夠緩存
保證該類能夠隨時訪問,可是它很難解決只有一個實例問題。最好的辦法就是讓該自身來負責保存它的惟一實例。這安全
個類必需要保證沒有其餘類來建立它。這裏咱們能夠將其構造方法私有化。即多線程
1 Public MyClass{ 2 PrivateMyClass(){} 3 }
含有私有化構造器的類就能保證它不能被其餘類實例化了。可是咱們如何來獲取這個實例化類呢?提供一個方法性能
用於返回該類的實例對象便可實現。spa
1 public class MyClass { 2 private MyClass(){ 3 4 } 5 6 public static MyClass getInstance(){ 7 return new MyClass(); 8 } 9 }
1、基本定義 線程
經過上面簡單介紹,咱們能夠對單例模式有一個簡單的認識。所謂單例模式就是確保某一個類只有一個實例,並設計
且提供一個全局訪問點。code
從上面能夠看出單例模式有以下幾個特色:
1、它只有一個實例。
2、它必需要自行實例化。
3、它必須自行想整個系統提供訪問點。
2、模式結構
單例模式能夠說是最簡單的設計模式了,它僅有一個角色Singleton。
Singleton:單例。
3、模式實現
1 public class Singleton { 2 //利用靜態變量來記錄Singleton的惟一實例 3 private static Singleton uniqueInstance; 4 5 /* 6 * 構造器私有化,只有Singleton類內才能夠調用構造器 7 */ 8 private Singleton(){ 9 10 } 11 12 public static Singleton getInstance(){ 13 if(uniqueInstance == null){ 14 uniqueInstance = new Singleton(); 15 } 16 17 return uniqueInstance; 18 } 19 20 }
在《Head First》有這樣一個場景,就是說有兩個線程都要執行這段代碼,頗有可能會產生兩個實例對象。以下圖:
這裏有三種解決方案。
第1、 使用synchronized來處理。也就是說將getInstance()方法變成同步方法便可。
1 public class Singleton { 2 //利用靜態變量來記錄Singleton的惟一實例 3 private static Singleton uniqueInstance; 4 5 /* 6 * 構造器私有化,只有Singleton類內才能夠調用構造器 7 */ 8 private Singleton(){ 9 10 } 11 12 public static synchronized Singleton getInstance(){ 13 if(uniqueInstance == null){ 14 uniqueInstance = new Singleton(); 15 } 16 17 return uniqueInstance; 18 } 19 20 }
第2、 直接初始化靜態變量。這樣就保證了線程安全。
1 public class Singleton { 2 /* 3 * 利用靜態變量來記錄Singleton的惟一實例 4 * 直接初始化靜態變量,這樣就能夠確保線程安全了 5 */ 6 private static Singleton uniqueInstance = new Singleton(); 7 8 /* 9 * 構造器私有化,只有Singleton類內才能夠調用構造器 10 */ 11 private Singleton(){ 12 13 } 14 15 public static Singleton getInstance(){ 16 return uniqueInstance; 17 } 18 19 }
第3、 用「雙重檢查加鎖」,在getInstance()中減小使用同步。
1 public class Singleton { 2 /* 3 * 利用靜態變量來記錄Singleton的惟一實例 4 * volatile 關鍵字確保:當uniqueInstance變量被初始化成Singleton實例時, 5 * 多個線程正確地處理uniqueInstance變量 6 * 7 */ 8 private volatile static Singleton uniqueInstance; 9 10 /* 11 * 構造器私有化,只有Singleton類內才能夠調用構造器 12 */ 13 private Singleton(){ 14 15 } 16 17 /* 18 * 19 * 檢查實例,若是不存在,就進入同步區域 20 */ 21 public static Singleton getInstance(){ 22 if(uniqueInstance == null){ 23 synchronized(Singleton.class){ //進入同步區域 24 if(uniqueInstance == null){ //在檢查一次,若是爲null,則建立 25 uniqueInstance = new Singleton(); 26 } 27 } 28 } 29 30 return uniqueInstance; 31 } 32 33 }
在這裏是首先檢查是否實例已經建立了,若是還沒有建立,纔會進行同步。這樣一來。只有第一次會同步。
4、模式優缺點
優勢
1、節約了系統資源。因爲系統中只存在一個實例對象,對與一些須要頻繁建立和銷燬對象的系統而言,單
例模式無疑節約了系統資源和提升了系統的性能。
2、由於單例類封裝了它的惟一實例,因此它能夠嚴格控制客戶怎樣以及什麼時候訪問它。
缺點
1、因爲單例模式中沒有抽象層,所以單例類的擴展有很大的困難。
2、單例類的職責太重,在必定程度上違背了「單一職責原則」。
5、模式使用場景
下列幾種狀況可使用單例模式。
1、系統只須要一個實例對象,如系統要求提供一個惟一的序列號生成器,或者須要考慮資源消耗太大而只容許建立一個對象。
2、客戶調用類的單個實例只容許使用一個公共訪問點,除了該公共訪問點,不能經過其餘途徑訪問該實例。
6、總結
1. 單例模式中確保程序中一個類最多隻有一個實例。
2. 單例模式的構造器是私有了,並且它必需要提供實例的全局訪問點。
3. 單例模式可能會由於多線程的問題而帶來安全隱患。