簡介
單例模式是指在程序中只有一個實例存在。並且在程序運行中的任何時候都可以獲取該實例對象。
場景
1.Android中數據庫多線程讀寫時,保證每個線程使用同一個SQLiteDatabase對象,否則會報錯“database is locked”;
2.Android常用框架EventBus中使用了單例模式。例如,當我們獲取EventBus對象時,會使用的EventBus.getDefault(),當我們查看它的源碼的時候,我們發現
static volatile EventBus defaultInstance;
...//此處省略
...
public static EventBus getDefault() {
if (defaultInstance == null) {
Class var0 = EventBus.class;
synchronized(EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
也就是EventBus使用了雙重校驗鎖的單例模式。此模式稍候介紹。
單例模式的實現方式
1.懶漢模式
特點:懶加載,不使用不創建,線程不安全
public class Singleton {
private static Singleton mSingleton = null;
private Singleton() {
}
public static Singleton getInstance() {
if (mSingleton == null)
mSingleton = new Singleton();
return mSingleton;
}
}
2.餓漢模式
特點:類加載的時候就創建對象,線程安全,沒有懶加載的靈活性,可能造成內存浪費
public class Singleton {
private static Singleton mSingleton = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return mSingleton;
}
}
3.雙重校驗鎖
特點:可以保證線程安全,但是需要使用volatile(JDK1.5引入)修飾對象的引用,因爲只是使用雙重校驗鎖時,也不能保證它是一個線程安全的單例模式。因爲JMM會有指令重排序機制。當我們創建一個對象的時候,JVM內部會將new Object()這條語句轉化爲若干指令,舉例:
指令1:先爲對象分配內存空間
指令2:初始化對象
指令3:設置對象所在的內存地址
以上是正常情況,如果發生了指令重排,可能指令3就會排在指令2的前面。
也就是先分配了內存地址,後初始化的對象。如果這時線程A指令執行的順序是1-3-2,那麼,當線程A執行到指令3的時候,mSingleton不爲null了。如果線程B執行到第一個if判斷的時候,發現mSingleton不爲空,這時就會出現問題。(見下面代碼)
public class Singleton {
private static Singleton mSingleton = null;
private Singleton() {
}
public Singleton getInstance() {
if (mSingleton == null) {
synchronized (Singleton.this) {
if (mSingleton == null)
mSingleton = new Singleton();
}
}
return mSingleton;
}
}
使用volatile關鍵字後,就可以禁止指令重排序(增加了內存屏障)
正確代碼:
public class Singleton {
private volatile static Singleton mSingleton = null;
private Singleton() {
}
public Singleton getInstance() {
if (mSingleton == null) {
synchronized (Singleton.this) {
if (mSingleton == null)
mSingleton = new Singleton();
}
}
return mSingleton;
}
}
4.靜態內部類
特點:類加載的時候就創建對象(此特點跟餓漢模式一樣),線程安全;懶加載,不使用不創建,減少內存開銷。
public class Singleton {
private Singleton() {
}
private static class SingleInstance {
private static final Singleton singleton = new Singleton();
}
public static final Singleton getInstance() {
return SingleInstance.singleton;
}
}
5.枚舉單例模式
特點:線程安全,防止了反序列化重新創建的新對象
public enum EmumSingleton {
SINGLETON;
EmumSingleton() {
}
public void doSomething(){
System.out.println("單例模式");
}
}
Over…