Android中的設計模式-單例模式

來源:互聯網
上載者:User

標籤:android   設計模式   單例模式   

Android中的設計模式-單例模式

單例模式算是比較常用的模式,在Java中如果想要一個JVM中只存在某個類的一個執行個體,就需要使用到單例模式,而只存在一個執行個體的需求一般是因為:
1,對象執行個體比較大和複雜,建立開銷很大。
2,只需要一個執行個體來維護整個功能的流程與互動。
例如Android中的電話應用啟動時,對於單卡單待的電話,只建立一個Phone對象,用來管理RIL,CallTracker,ServiceStateTracker等對象,手機中不存在第二個Phone對象去和RILC通訊。

類圖

單例的類圖很簡單,看起來也很簡單,只需要一個類只能得到一個執行個體即可,但是我覺得單例比其他建立型的模式要複雜的多。
如果想要建立一個類Singleton。正常來講只需要new Singleton()即可,但是如果想Singleton只存在一個執行個體,則不能採用這種方法來建立,因為每一次new都會產生一個新的執行個體:
1,為了不能使用new建立,就要把建構函式變成private的;
2, 為了只有一個執行個體,就需要Singleton本身去維護這個執行個體,於是類中需要定義一個Singleton instance,顯然它應該是private的,因為它是類的執行個體,不是對象的執行個體,所以它還應該是靜態;
3,因為不能new,為了能夠有方法得到Singleton的執行個體,就得通過一個靜態方法返回執行個體,比如public static Singleton getInstance(),在內部如果instance是null的則建立,否則返回instance即可,

說了這麼多其實需要注意的東西還是蠻多的,根據上面的分析,已經能夠建立出一個最簡單單例模式了。

普通餓漢單例模式
public class Singleton {private static Singleton instance = new Singleton();private Singleton() {// Nothing}public static Singleton getInstance(){return instance;}public void say(){System.out.println("I am singleton");}}

餓漢單例會在類裝載時就執行個體化。好處就是由於classloder機制,保證當一個類被載入的時候,這個類的載入是線程互斥的,而餓漢單例的靜態instance直接建立一個執行個體,在載入的時候就能安全執行緒的獲得執行個體。從而避免了安全執行緒問題。
劣勢就是執行個體在裝載的時候就會浪費資源和時間去執行個體化。雖然大多數時候都是在調用getInstance時才會裝載,不過也沒法保證是否有其他方法會使用到instance而導致建立執行個體。

惰性載入單例模式

這個Singleton單例類在被載入的時候就會建立執行個體,為了讓它在使用的時候才建立對象所以把它設為null。讓它在getInstace的時候再建立執行個體,也就是惰性載入。這裡也叫懶漢式單例。

public class lazySingleton {private static lazySingleton instance = null;private lazySingleton() {// TODO Auto-generated constructor stub}public static lazySingleton getInstace() {if (instance==null){instance =new lazySingleton();}return instance;}public void say(){System.out.println("I am lazySingleton");}}
加鎖單例模式

前面的懶漢例子用在單線程中不會出現問題,但是如果用在單線程中就會出現問題。如果AB兩個線程都通過getInstace去擷取單例的執行個體,因為沒法保證getInstace方法會在一個線程中一直執行完再執行另一個線程,如果兩個線程都判定instance位null,則有可能都會進入new語句建立執行個體。
為了保證同一時刻只有一個線程能夠執行getInstance,就需要對方法加鎖,或者在方法內部使用對象鎖,但是注意要把鎖放在if(instance == null) 判斷的外面,否則還是可能出現同時判定true的情況。

public class SyncSingleton {private static SyncSingleton instance = null;private SyncSingleton() {// TODO Auto-generated constructor stub}public static synchronized SyncSingleton getInstance(){if (instance == null){instance = new SyncSingleton();}return instance;}}

或者

public class SyncSingleton {      private static SyncSingleton instance = null;      private static final Object classLock = SyncSingleton.class;     private SyncSingleton(){}      public SyncSingleton getInstance(){          synchronized(classLock){              if(instance == null)                  instance = new SyncSingleton();              return instance;          }      }  }  
雙重檢查加鎖單例

上面的問題解決了多線程的問題,但是也會帶來效能的問題,因為每次調用getInstance,都會進行同步,但實際上,如果instance執行個體已經建立,那直接返回instance執行個體就好,這裡是不用加鎖的,只有建立執行個體的情況才需要同步鎖,但是前面也說到了,建立的時候要把鎖放在if(instance == null) 判斷的外面,否則還是可能出現同時判定true的情況。所以就有了下面的雙重檢查的單例模式

public class EffectiveSingleton {    private volatile static EffectiveSingleton instance = null;    private EffectiveSingleton() {        // TODO Auto-generated constructor stub    }    public static EffectiveSingleton getInstance() {        if (instance == null) { // 首先判斷是否已經建立執行個體,如果已經建立,直接返回,效率高            synchronized (EffectiveSingleton.class) {// 如果沒有建立,然後再同步,並在同步塊內再初始化。注意要再次判斷是否已執行個體化                if (instance == null) {                    instance = new EffectiveSingleton();                }            }        }        return instance;    }}

這樣當執行個體初始化已經完成的情況,每次getInstance直接返回即可,不再需要同步鎖。
當第一次調用getInstance時,則通過synchronized塊包裹的代碼部分保證不會多次調用建立執行個體。
第二個if (instance == null) 條件判斷是為了第一個if (instance == null) 是給已經存在執行個體的情況用的,AB線程還是有可能都通過第一個條件判斷的。這就需要在同步塊內,一定要有一個條件判斷。

雙重檢查加鎖單例模式很好地解決了加鎖單例的效能問題。

使用靜態內部類的單例模式

靜態內部類也叫嵌套類,用這個名字給他定義是更加形象的。意思是說內部類和外部類的關係只是層次嵌套關係,所以只是在建立類檔案的時候類檔案名稱是如下形式:outer$inner.java,在使用方面完全和兩個普通類一樣。
在餓漢單例的基礎上,把instance = new Singleton()外用一個叫做SingletonHolder 的靜態內部類封裝一下。
這樣即使在Singleton類載入的時候,也不會導致靜態內部類SingletonHolder 的載入,只有在使用到SingletonHolder 的時候,才會載入。
同時由於類的載入的機制的互斥性,保證建立執行個體時候的執行緒安全性。

class Singleton {    private static class SingletonHolder {        private static final Singleton instance = new Singleton();    }    private Singleton() {    }    public static final Singleton getInstance() {        return SingletonHolder.instance;    }}
Android中的單例

通過PhoneFactory建立Phone對象的例子,截取部分如下:

public class PhoneFactory {    static private Phone sProxyPhone = null;    ......    public static void makeDefaultPhone(Context context) {         synchronized(Phone.class) {                if (!sMadeDefaults) {                ...                sProxyPhone = new PhoneProxy(new GSMPhone(context,                                sCommandsInterface, sPhoneNotifier));                ...                sMadeDefaults = true;                }            }        }

可以看出這個例子符合上面的加鎖單例模式,雖然不是採用的雙重判斷的方式來增加效率,但是因為PhoneFactory的makeDefaultPhone基本沒有多線程使用方式,只有在Phone應用啟動的情況下調用一起。
並且PhoneFactory通過makeDefaultPhone來建立執行個體,但是卻使用getDefaultPhone來擷取執行個體,也就不存在執行個體已經存在的情況下,還進入同步塊進行判斷的情況。

Android中的設計模式-單例模式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.