先不論單例模式的寫法,有些方面是相同的,比如都需要將唯一的對(duì)象設(shè)置為static的,都需要將構(gòu)造方法private化,代碼如下:
在寧縣等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場(chǎng)前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供做網(wǎng)站、成都網(wǎng)站設(shè)計(jì) 網(wǎng)站設(shè)計(jì)制作按需求定制網(wǎng)站,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計(jì),成都全網(wǎng)營(yíng)銷推廣,外貿(mào)營(yíng)銷網(wǎng)站建設(shè),寧縣網(wǎng)站建設(shè)費(fèi)用合理。
public class MyInstance { private static MyInstance instance; private MyInstance(){}
}
第一種:最原始的單例模式,代碼如下:
public static MyInstance getInstance(){ if(instance==null){ instance=new MyInstance();
} return instance;
}
多線程并發(fā)時(shí),可能會(huì)出現(xiàn)重復(fù)new對(duì)象的情況,因此不提倡使用。
第二種:將整個(gè)方法塊進(jìn)行加鎖,保證線程安全。
public static synchronized MyInstance getInstance(){ if(instance==null){ instance=new MyInstance();
} return instance;
}
這種代碼下,每條線程都會(huì)依次進(jìn)入方法塊內(nèi)部,雖然實(shí)現(xiàn)了單例,但是影響了運(yùn)行效率,可以使用但是也不怎么提倡。
第三種:進(jìn)一步優(yōu)化的方法。
public static MyInstance getsInstance(){ synchronized (MyInstance.class){ if(instance==null){ instance=new MyInstance(); return instance;
}else{ return instance;
}
}
}
這種方式只是第二種方法的一種優(yōu)化,但是優(yōu)化有限。
(以下的幾種方法比較推薦使用)
第四種:雙層判斷加鎖,效率影響小且保證了線程安全。
public static MyInstance getsInstance() { if (instance == null) { synchronized (MyInstance.class) { if(instance==null){ instance=new MyInstance();
}
}
} return instance;
}
這種方法是對(duì)第二種和第三種方法的進(jìn)一步優(yōu)化,比較推薦使用。
第五種:內(nèi)部類實(shí)現(xiàn)單例,不用線程鎖來(lái)實(shí)現(xiàn)效率的提升。
public class MyInstance { private MyInstance() {
} public static MyInstance getInstance(){ return MyInstanceHolder.instance;
} private static class MyInstanceHolder{ private static MyInstance instance=new MyInstance();
}
}
在內(nèi)部類中new對(duì)象,再將內(nèi)部類的對(duì)象返回,這種方法是使用了java中class加載時(shí)互斥的原理來(lái)實(shí)現(xiàn)了線程的安全。不加線程鎖也使得運(yùn)行效率不會(huì)受到較大的影響。比較提倡。
java模式之單例模式:
單例模式確保一個(gè)類只有一個(gè)實(shí)例,自行提供這個(gè)實(shí)例并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。
特點(diǎn):
1,一個(gè)類只能有一個(gè)實(shí)例
2,自己創(chuàng)建這個(gè)實(shí)例
3,整個(gè)系統(tǒng)都要使用這個(gè)實(shí)例
Singleton模式主要作用是保證在Java應(yīng)用程序中,一個(gè)類Class只有一個(gè)實(shí)例存在。在很多操作中,比如建立目錄
數(shù)據(jù)庫(kù)連接都需要這樣的單線程操作。一些資源管理器常常設(shè)計(jì)成單例模式。
外部資源:譬如每臺(tái)計(jì)算機(jī)可以有若干個(gè)打印機(jī),但只能有一個(gè)Printer
Spooler,以避免兩個(gè)打印作業(yè)同時(shí)輸出到打印機(jī)中。每臺(tái)計(jì)算機(jī)可以有若干個(gè)通信端口,系統(tǒng)應(yīng)當(dāng)集中管理這些通信端口,以避免一個(gè)通信端口被兩個(gè)請(qǐng)求同時(shí)調(diào)用。內(nèi)部資源,譬如,大多數(shù)的軟件都有一個(gè)(甚至多個(gè))屬性文件存放系統(tǒng)配置。這樣的系統(tǒng)應(yīng)當(dāng)由一個(gè)對(duì)象來(lái)管理這些屬性文件。
一個(gè)例子:Windows
回收站。
在整個(gè)視窗系統(tǒng)中,回收站只能有一個(gè)實(shí)例,整個(gè)系統(tǒng)都使用這個(gè)惟一的實(shí)例,而且回收站自行提供自己的實(shí)例。因此,回收站是單例模式的應(yīng)用。
兩種形式:
1,餓漢式單例類
public class Singleton {
private Singleton(){}
//在自己內(nèi)部定義自己一個(gè)實(shí)例,是不是很奇怪?
//注意這是private 只供內(nèi)部調(diào)用
private static Singleton instance =
new Singleton();
//這里提供了一個(gè)供外部訪問(wèn)本class的靜態(tài)方法,可以直接訪問(wèn)
public static Singleton getInstance() {
return instance;
}
}
2,懶漢式單例類
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton
getInstance() {
//這個(gè)方法比上面有所改進(jìn),不用每次都進(jìn)行生成對(duì)象,只是第一次
//使用時(shí)生成實(shí)例,提高了效率!
if (instance==null)
instance=new Singleton();
return instance; }
}
第二中形式是lazy initialization,也就是說(shuō)第一次調(diào)用時(shí)初始Singleton,以后就不用再生成了。
注意到lazy
initialization形式中的synchronized,這個(gè)synchronized很重要,如果沒(méi)有synchronized,那么使用getInstance()是有可能得到多個(gè)Singleton實(shí)例。
一般來(lái)說(shuō)第一種比較安全
我自己比較常用的方式:
public class Singleton {
private volatile static
Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if(singleton==null){
synchronized(Singleton.class){
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
單例模式常見(jiàn)的兩種實(shí)現(xiàn)方式 餓漢模式和 雙重鎖模式
?餓漢模式
public class HungrySingleton {
private static HungrySingleton mInstance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return mInstance;
}
}
不得不說(shuō),餓漢模式這個(gè)名字起得的確很巧,這種方式,不管你用不用得著這個(gè)實(shí)例,先給你創(chuàng)建(new)出來(lái),生怕將來(lái)創(chuàng)建沒(méi)機(jī)會(huì)似得,完全就是今朝有酒今朝醉的節(jié)奏。
與上面對(duì)應(yīng)的還有一種就是懶漢模式,就是在用的時(shí)候才在getInstance 方法中完成實(shí)例的創(chuàng)建(new),真是“懶”,同時(shí)給這個(gè)方法添加synchronized 關(guān)鍵字,可以確保在多線程情況下單例依舊唯一,但是懶漢模式每次調(diào)用getInstance 方法時(shí)由于synchronized 的存在,需要進(jìn)行同步,造成不必要的資源開銷。因此便有了下面雙重鎖模式的實(shí)現(xiàn)方式。
?雙重鎖模式(DCL 實(shí)現(xiàn))
public class LazySingleton {
private static LazySingleton mInstance = null;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (mInstance == null) {
synchronized (LazySingleton.class) {
if (mInstance == null) {
mInstance = new LazySingleton();
}
}
}
return mInstance;
}
}
這樣既避免了餓漢模式的缺點(diǎn),又解決了懶漢模式的不足;確保單例只在第一次真正需要的時(shí)候創(chuàng)建。
Android 中的使用
在日常的Android開發(fā)中,也可以見(jiàn)到單例模式的身影。
?Glide
使用Glide加載圖片非常方便,大家應(yīng)該不陌生,可以看一下它的源碼中單例模式的實(shí)現(xiàn)方式。
Glide.with(this).load(url).into(imageView);
//Glide.with()
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
//RequestManagerRetriever.get()
/** The singleton instance of RequestManagerRetriever. */
private static final RequestManagerRetriever INSTANCE = new RequestManagerRetriever();
/**
* Retrieves and returns the RequestManagerRetriever singleton.
*/
public static RequestManagerRetriever get() {
return INSTANCE;
}
可以看到,當(dāng)我們寫下Glide.with(..) 這行代碼時(shí),就完成了RequestManagerRetriever 這個(gè)類的實(shí)例化,這個(gè)類的單例模式是使用餓漢模式實(shí)現(xiàn)的。
?EventBus
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
};
很明顯,EventBus的單例模式使用雙重鎖模式實(shí)現(xiàn)的。
?InputMethodManager static InputMethodManager sInstance
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
sInstance = new InputMethodManager(service, Looper.getMainLooper());
}
return sInstance;
}
}
InputMethodManager 的單例模式是使用懶漢模式實(shí)現(xiàn)。
可以看到,關(guān)于單例模式的實(shí)現(xiàn)方式,面對(duì)不同的場(chǎng)景,我們可以做出不同的選擇
?Glide的單例模式雖然是使用餓漢模式實(shí)現(xiàn),但理論上來(lái)說(shuō)并不會(huì)造成內(nèi)存資源的浪費(fèi),因?yàn)楫?dāng)我們通過(guò)gradle的配置引入Glide的庫(kù)時(shí),就是為了加載圖片,必然會(huì)使用Glide.with進(jìn)行相關(guān)的操作。同時(shí)RequestManagerRetriever 這個(gè)類應(yīng)該是一個(gè)網(wǎng)絡(luò)請(qǐng)求的管理類(Glide源碼沒(méi)有研究過(guò),這里只是猜測(cè)),這樣的一個(gè)類必然需要使用單列模式,試想如果存在多個(gè)管理類的實(shí)例,那么談何管理,那么的多Request到底聽哪個(gè)manger 的,這就是前面提到必須使用單列模式的情景。
?EventBus 作為事件總線的更要使用單例模式了,如果說(shuō)EventBus的實(shí)例不是單例模式,那么他就無(wú)法實(shí)現(xiàn)它的功能了。對(duì)于EventBus不了解的同學(xué),可以看看
EventBus 3.0 相見(jiàn)恨晚,EventBus真的很強(qiáng)大。
?InputMethodManager 使用懶漢模式實(shí)現(xiàn)單例也是無(wú)可厚非的,畢竟誰(shuí)會(huì)去頻繁的獲取那么多他的實(shí)例呢;同時(shí)作為一個(gè)系統(tǒng)的輸入法管理器,他也必須是唯一的,因此這個(gè)類也需要單例模式來(lái)實(shí)現(xiàn)它唯一的實(shí)例供外部使用。
由上可見(jiàn),關(guān)于單例模式的實(shí)現(xiàn),沒(méi)有說(shuō)哪一種方式最好,只有最合適的實(shí)現(xiàn)方式;實(shí)際開發(fā)中,單例模式應(yīng)該怎么寫,還需要根據(jù)業(yè)務(wù)場(chǎng)景做最合適的選擇,無(wú)論是餓漢懶漢實(shí)用才是好漢。個(gè)人感覺(jué),餓漢模式是一種簡(jiǎn)單又方便的實(shí)現(xiàn)方式, 一個(gè)類既然已經(jīng)寫成了單例模式,必然是要使用的呀,誰(shuí)會(huì)去創(chuàng)建一個(gè)餓漢模式的單例,又不去使用這個(gè)單例呢?
之前在使用Volley的時(shí)候,就是使用餓漢模式創(chuàng)建整個(gè)應(yīng)用的RequestQueue單例,所有需要網(wǎng)絡(luò)請(qǐng)求的地方,把request添加到RequestQueue單例中即可。
public class MyApplication extends Application{
// 建立請(qǐng)求隊(duì)列
public static RequestQueue queue;
@Override
public void onCreate() {
super.onCreate();
queue = Volley.newRequestQueue(getApplicationContext());
}
public static RequestQueue getHttpQueue() {
return queue;
}
}
在應(yīng)用Application的onCreate方法中創(chuàng)建了屬于整個(gè)應(yīng)用的queue,之后每一次網(wǎng)絡(luò)請(qǐng)求時(shí),只需要queue.add(Request)即可,這里使用單例模式,可以有效的避免在多個(gè)地方創(chuàng)建RequestQueue 的實(shí)例,浪費(fèi)系統(tǒng)資源。
單例模式(Singleton)
一、 什么是單例模式
單例模式,簡(jiǎn)單點(diǎn)來(lái)說(shuō)就是設(shè)計(jì)一個(gè)類,使其在任何時(shí)候,最多只有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)這個(gè)實(shí)例的全局訪問(wèn)點(diǎn)。
二、 為什么要單例
在程序中的很多地方,只有一個(gè)實(shí)例是非常重要的。例如,在windows中,任務(wù)管理器只有一個(gè),無(wú)論你點(diǎn)擊多少次打開任務(wù)管理器,任務(wù)管理器也只會(huì)生成一個(gè)窗口。再例如,在一些軟件中,工具箱是唯一的,無(wú)論你點(diǎn)擊多少次打開工具箱,工具箱也只一個(gè)。
為什么要這樣設(shè)計(jì)呢?因?yàn)橄袢蝿?wù)管理器或工具箱這樣的程序,只要有一個(gè)就足夠完成所有的工作了,多個(gè)程序只會(huì)白白消耗系統(tǒng)資源,而像任務(wù)管理器這類的程序還會(huì)引入多個(gè)任務(wù)管理器之間的同步問(wèn)題,所以對(duì)些這些程序來(lái)說(shuō),只有一個(gè)實(shí)例或程序是必要的。
三、 為什么需要單例模式
上面講到對(duì)于某些程序來(lái)說(shuō),保持其只有一個(gè)實(shí)例是必要的,但是如何保證一個(gè)程序或一個(gè)類只有一個(gè)實(shí)例呢?下面從類的角度來(lái)解說(shuō)。