Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中的設計模式-單例模式

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

編輯:關於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即可,<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPsu1wcvV4sO0tuDG5Mq10OjSqtei0uK1xLarzve7ucrHwvm24LXEo6y4+b7dyc/D5rXEt9bO9qOs0tG+rcTcubu0tL2os/bSu7j21+688rWltaXA/cSjyr3By6GjPC9wPg0KPGgxIGlkPQ=="普通餓漢單例模式">普通餓漢單例模式

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來獲取實例,也就不存在實例已經存在的情況下,還進入同步塊進行判斷的情況。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved