1.單例模式的概念
單例模式其實(shí)是一個(gè)類(lèi)只有一個(gè)實(shí)例侦铜,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例的設(shè)計(jì)模式。是最簡(jiǎn)單也是應(yīng)用最廣的設(shè)計(jì)模式函卒。一般用于避免產(chǎn)生多個(gè)對(duì)象消耗系統(tǒng)資源或者要求某種類(lèi)型的對(duì)象必須獨(dú)一無(wú)二的場(chǎng)景辆憔。
2.單例模式常見(jiàn)寫(xiě)法
單例模式有很多種寫(xiě)法,懶漢式报嵌、餓漢式虱咧、雙重校驗(yàn)鎖(DCL)、類(lèi)級(jí)內(nèi)部沪蓬、枚舉等等彤钟,這里我們只講上面說(shuō)的的這5種。
2.1 懶漢式
體現(xiàn)的編程思想
1.懶加載
2.緩存思想
public class LazySingleton {
//定義一個(gè)變量類(lèi)存儲(chǔ)創(chuàng)建好的實(shí)例對(duì)象 默認(rèn)為空
private static LazySingleton singleton = null;
//私有構(gòu)造函數(shù) 避免外部創(chuàng)建
private LazySingleton(){
}
//提供一個(gè)類(lèi)的靜態(tài)方法返回單例對(duì)象 全局可訪問(wèn)
public static LazySingleton getInstance(){
//懶體現(xiàn)在第一次需要用到這個(gè)對(duì)象的時(shí)候才會(huì)去創(chuàng)建 以后不會(huì)重新初始化對(duì)象
if (singleton==null) {
singleton = new LazySingleton();
}
return singleton;
}
}
2.2 餓漢式
static修飾符的特點(diǎn)
1.static修飾的變量在類(lèi)裝載的時(shí)候初始化
2.多個(gè)實(shí)例的static變量會(huì)共享那個(gè)同一塊內(nèi)存
餓漢式是線程安全的跷叉,因?yàn)樘摂M機(jī)的類(lèi)加載機(jī)制
public class HungerSingleton {
//類(lèi)加載的時(shí)候創(chuàng)建實(shí)例
private static HungerSingleton singleton = new HungerSingleton();
private HungerSingleton(){
}
public static HungerSingleton getInstance(){
//ClassLoader機(jī)制保證了實(shí)例只有一個(gè)
return singleton;
}
}
2.3 DCL雙重校驗(yàn)鎖
懶漢式是線程不安全的:比如兩個(gè)線程同時(shí)訪問(wèn)懶漢單例逸雹,在A線程的單例沒(méi)有創(chuàng)建完成的時(shí)候同時(shí)進(jìn)入了防空判斷,單例控制就會(huì)失效云挟。
public class DCLSycSingleton {
//volatile修飾的變量不會(huì)被本地線程保存
private volatile static DCLSycSingleton singleton = null;
private DCLSycSingleton(){
}
public static DCLSycSingleton getInstance(){
//判斷是否為空 否則進(jìn)入代碼塊
if (singleton==null) {
//同步塊 線程安全的創(chuàng)建實(shí)例 只允許一次線程操作
synchronized (DCLSycSingleton.class){
//再次檢查是否為空
if (singleton==null) {
singleton = new DCLSycSingleton();
}
}
}
return singleton;
}
}
2.4 類(lèi)級(jí)內(nèi)部類(lèi)
多線程開(kāi)發(fā)的同步控制:
1.synchronized同步鎖梆砸,這個(gè)比較常見(jiàn)
2.由靜態(tài)初始化器(靜態(tài)字段或static{}代碼塊的初始化器)初
始化數(shù)據(jù)時(shí)
3.訪問(wèn)final字段時(shí)
4.在創(chuàng)建線程之前創(chuàng)建對(duì)象時(shí)
5.線程可以看見(jiàn)它將要處理的對(duì)象時(shí)
- 思路:
類(lèi)級(jí)內(nèi)部類(lèi)和多線程缺省同步鎖,靜態(tài)初始化器由虛擬機(jī)保證線程安全园欣,由類(lèi)級(jí)內(nèi)部?jī)?nèi)的方式創(chuàng)建實(shí)例帖世,不調(diào)用到這個(gè)類(lèi)級(jí)內(nèi)部類(lèi)就不會(huì)創(chuàng)建實(shí)例,這樣就保證了懶加載和線程安全
/**
* 類(lèi)級(jí)內(nèi)部類(lèi) 該類(lèi)的實(shí)例與外部類(lèi)的實(shí)例沒(méi)有依賴 而且只有在被調(diào)用的時(shí)候才會(huì)被裝載沸枯,從而實(shí)現(xiàn)懶加載
*/
private static class Singleton{
//靜態(tài)初始化器 由虛擬機(jī)保證線程安全
private static HolderSingleton singleton = new HolderSingleton();
}
private HolderSingleton(){
}
public static HolderSingleton getInstance(){
return Singleton.singleton;
}
2.5 枚舉
枚舉元素就是一個(gè)單例日矫,由虛擬機(jī)提供序列化機(jī)制,是最好的單例寫(xiě)法绑榴。
public enum EnumSingleton{
INSTANCE;
public void doSomethings(){
//...做一些功能或者其它
}
}
3.單例模式在Android中的運(yùn)用
1.Application
用戶重寫(xiě)的Application有且只有一個(gè)
2.Activity
啟動(dòng)模式設(shè)置為singleTask的Activity在task中只能存在一個(gè)實(shí)例
3.Service
bindService()啟動(dòng)的Service哪轿,再次啟動(dòng)只會(huì)調(diào)用onStartCommand(),而不會(huì)調(diào)用onCreate()
4.各種Manager
比如WindowManager翔怎、ActivityManager窃诉、PowerManager、ServiceManager等赤套,這些管理類(lèi)對(duì)資源進(jìn)行操作飘痛,為了避免對(duì)同一資源進(jìn)行
操作,而且為了減少資源消耗容握,都采用了單例模式
5.UID
Universal-Image-Loader老牌的圖片加載框架
6.其它的比如EventBus
這個(gè)著名的事件總線框架使用的是DCL的單例模式
4.開(kāi)發(fā)中如何確定使用單例模式
1.當(dāng)創(chuàng)建一個(gè)對(duì)象需要耗費(fèi)過(guò)多的資源或者一個(gè)對(duì)象需要被反復(fù)創(chuàng)建宣脉、銷(xiāo)毀的時(shí)候,如讀取配置等,可以創(chuàng)建一個(gè)單例常駐內(nèi)存,減少資源浪費(fèi)缅刽;
2.當(dāng)需要對(duì)同一個(gè)資源進(jìn)行管理如File叫挟、I/O操作,可以創(chuàng)建單例埃儿,避免對(duì)一個(gè)對(duì)象同時(shí)操作。
5.需要注意的問(wèn)題
單例必定有static操作符,如果持有Activity的context践险,很可能造成Activity無(wú)法釋放,導(dǎo)致OOM吹菱,盡量使用Application的context巍虫;比如有的人喜歡維護(hù)一個(gè)Activity的棧用來(lái)管理Activity,使用不當(dāng)或者一些意外情況會(huì)導(dǎo)致Activity并沒(méi)有被remove鳍刷。
以上就是本人對(duì)單例模式的總結(jié)和思考占遥,能力有限,如有紕漏输瓜,謝謝指出瓦胎。