單例模式的作用
有些對象的實例只需要一個,如緩存,數(shù)據(jù)庫等,如果有多個實例就會產(chǎn)生許多問題,單例模式確保了類只有一個實例,并給了我們一個全局的訪問點。
相對與全局變量,利用單例模式可以延遲創(chuàng)建對象,
一個簡單的單例模式
public class Singleton {
private static Singleton uniqueInstance;
private Singleton(){};
public static Singleton getInstance(){
if (null == uniqueInstance){
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
}
單例模式創(chuàng)建步驟如下:
- 利用一個靜態(tài)變量來記錄Singleton類的唯一實例。
- 把構(gòu)造器聲明為私有的,只要在Singleton類內(nèi)部才可以調(diào)用。
- 用getInstance()方法實例化對象,并返回這個實例。
單例模式多線程的問題
如果我們開啟多個線程,調(diào)用getInstance()方法,可能多有多個線程同時進(jìn)入這個方法,這樣就創(chuàng)建了不同的實例。
- getInstance()改為同步方法
public static synchronized Singleton getInstance(){}
通過增加 synchronized到getInstance()方法中,迫使每個線程進(jìn)入到這個方法之前,需要等待別的線程離開該方法,即不會有兩個線程可以同時進(jìn)入該方法。
缺點:只有第一次調(diào)用創(chuàng)建實例時才需要同步,之后每次這個方法,同步都是一種累贅,一個同步方法可能造成程序執(zhí)行效率下降100倍。
改善多線程
1.如果調(diào)用getInstance()對應(yīng)用程序不是很關(guān)鍵,則加上synchronized。
2.如果getInstance()比較頻繁,使用"急切"創(chuàng)建實例,而不使用延遲做法。
private static Singleton uniqueInstance = new Singleton();
private Singleton(){};
public static synchronized Singleton getInstance(){
return uniqueInstance;
}
使用這個方法時,JVM在加載這個類時馬上創(chuàng)建此類的單例實例,JVM保證任何線程訪問uniqueInstance靜態(tài)變量前,一定會創(chuàng)建此實例。
3.用"雙重檢查加鎖"
首先檢查實例是否已經(jīng)創(chuàng)建了,如果沒有創(chuàng)建,才進(jìn)行同步,這樣只有第一次才會同步。
private volatile static Singleton uniqueInstance;
//保證了不同線程對這個變量進(jìn)行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的
private Singleton(){};
public static synchronized Singleton getInstance(){
if (null == uniqueInstance){
//如果不存在進(jìn)入同步
synchronized (Singleton.class) {
//再次檢查,如果不存在才創(chuàng)建
if ( null == uniqueInstance){
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}