1.單例模式的概念
單例模式其實是一個類只有一個實例,而且自行實例化并向整個系統提供這個實例的設計模式。是最簡單也是應用最廣的設計模式。一般用于避免產生多個對象消耗系統資源或者要求某種類型的對象必須獨一無二的場景。
2.單例模式常見寫法
單例模式有很多種寫法,懶漢式、餓漢式、雙重校驗鎖(DCL)、類級內部、枚舉等等,這里我們只講上面說的的這5種。
2.1 懶漢式
體現的編程思想
1.懶加載
2.緩存思想
public class LazySingleton {
//定義一個變量類存儲創建好的實例對象 默認為空
private static LazySingleton singleton = null;
//私有構造函數 避免外部創建
private LazySingleton(){
}
//提供一個類的靜態方法返回單例對象 全局可訪問
public static LazySingleton getInstance(){
//懶體現在第一次需要用到這個對象的時候才會去創建 以后不會重新初始化對象
if (singleton==null) {
singleton = new LazySingleton();
}
return singleton;
}
}
2.2 餓漢式
static修飾符的特點
1.static修飾的變量在類裝載的時候初始化
2.多個實例的static變量會共享那個同一塊內存
餓漢式是線程安全的,因為虛擬機的類加載機制
public class HungerSingleton {
//類加載的時候創建實例
private static HungerSingleton singleton = new HungerSingleton();
private HungerSingleton(){
}
public static HungerSingleton getInstance(){
//ClassLoader機制保證了實例只有一個
return singleton;
}
}
2.3 DCL雙重校驗鎖
懶漢式是線程不安全的:比如兩個線程同時訪問懶漢單例,在A線程的單例沒有創建完成的時候同時進入了防空判斷,單例控制就會失效。
public class DCLSycSingleton {
//volatile修飾的變量不會被本地線程保存
private volatile static DCLSycSingleton singleton = null;
private DCLSycSingleton(){
}
public static DCLSycSingleton getInstance(){
//判斷是否為空 否則進入代碼塊
if (singleton==null) {
//同步塊 線程安全的創建實例 只允許一次線程操作
synchronized (DCLSycSingleton.class){
//再次檢查是否為空
if (singleton==null) {
singleton = new DCLSycSingleton();
}
}
}
return singleton;
}
}
2.4 類級內部類
多線程開發的同步控制:
1.synchronized同步鎖,這個比較常見
2.由靜態初始化器(靜態字段或static{}代碼塊的初始化器)初
始化數據時
3.訪問final字段時
4.在創建線程之前創建對象時
5.線程可以看見它將要處理的對象時
- 思路:
類級內部類和多線程缺省同步鎖,靜態初始化器由虛擬機保證線程安全,由類級內部內的方式創建實例,不調用到這個類級內部類就不會創建實例,這樣就保證了懶加載和線程安全
/**
* 類級內部類 該類的實例與外部類的實例沒有依賴 而且只有在被調用的時候才會被裝載,從而實現懶加載
*/
private static class Singleton{
//靜態初始化器 由虛擬機保證線程安全
private static HolderSingleton singleton = new HolderSingleton();
}
private HolderSingleton(){
}
public static HolderSingleton getInstance(){
return Singleton.singleton;
}
2.5 枚舉
枚舉元素就是一個單例,由虛擬機提供序列化機制,是最好的單例寫法。
public enum EnumSingleton{
INSTANCE;
public void doSomethings(){
//...做一些功能或者其它
}
}
3.單例模式在Android中的運用
1.Application
用戶重寫的Application有且只有一個
2.Activity
啟動模式設置為singleTask的Activity在task中只能存在一個實例
3.Service
bindService()啟動的Service,再次啟動只會調用onStartCommand(),而不會調用onCreate()
4.各種Manager
比如WindowManager、ActivityManager、PowerManager、ServiceManager等,這些管理類對資源進行操作,為了避免對同一資源進行
操作,而且為了減少資源消耗,都采用了單例模式
5.UID
Universal-Image-Loader老牌的圖片加載框架
6.其它的比如EventBus
這個著名的事件總線框架使用的是DCL的單例模式
4.開發中如何確定使用單例模式
1.當創建一個對象需要耗費過多的資源或者一個對象需要被反復創建、銷毀的時候,如讀取配置等,可以創建一個單例常駐內存,減少資源浪費;
2.當需要對同一個資源進行管理如File、I/O操作,可以創建單例,避免對一個對象同時操作。
5.需要注意的問題
單例必定有static操作符,如果持有Activity的context,很可能造成Activity無法釋放,導致OOM,盡量使用Application的context;比如有的人喜歡維護一個Activity的棧用來管理Activity,使用不當或者一些意外情況會導致Activity并沒有被remove。
以上就是本人對單例模式的總結和思考,能力有限,如有紕漏,謝謝指出。