單例模式使用比較常見,用來保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。在Android application包中有個Bluetooth相關的包就用到了單例模式,實例代碼如下:
public class BluetoothOppManager {
private static BluetoothOppManager INSTANCE;
/** Used when obtaining a reference to the singleton instance. */
private static Object INSTANCE_LOCK = new Object();
/**
* Get singleton instance.
*/
public static BluetoothOppManager getInstance(Context context) {
synchronized (INSTANCE_LOCK) {
if (INSTANCE == null) {
INSTANCE = new BluetoothOppManager();
}
INSTANCE.init(context);
return INSTANCE;
}
}
}
這裏考慮到了多線程互斥的問題,引入了一個靜態只讀的進程輔助對象。它使得最先進入的那個線程來創建這個實例,以後的線程進入時不會創建實例對象。
不知道細心的讀者發現沒,這個getInstance()操作,每次被調用時,都會加上同步鎖,這樣會影響性能,所以有些改進的辦法,見下文:
public static BluetoothOppManager getInstance(Context context) {
if(INSTANCE == null)
synchronized (INSTANCE_LOCK) {
if (INSTANCE == null) {
INSTANCE = new BluetoothOppManager();
}
INSTANCE.init(context);
}
return INSTANCE;
}
}
這種做法只有在實例未被創建的時候才加鎖,同時也能保證多線程的安全,所以該做法又叫Double-Check Locking(雙重鎖定)。這時又有同學要問,爲什麼我前面已經判斷了INSTANCE是否爲null,爲什麼同步代碼裏面又做了一次判斷?這個也不難理解,當INSTANCE爲null時,兩個線程同時調用,這時它們都可以通過第一輪判斷,都會立馬加鎖,但是最先進入臨界區的線程先加鎖,後進入的等待之。知道先進去的哥們創建好之後出來釋放爲止。此時,INSTANCE已經被創建,所以後進去的哥們被喚醒時,一看INSTANCE已經被創建了,那我就不創建了,所以也就避免了被創建兩次的尷尬。
最後附上單例模式的結構圖: