單例模式不用多說,就是隻存在一個對象,在任何情況下,拿到這個對象的實例都是同一個對象。
首先看最簡單的情況,該對象只會同時被一個線程訪問。我們可以寫出如下的單例模式的代碼:
public class SingleTon {
private SingleTon(){}
private static SingleTon instance ;
public static SingleTon getInstance(){
if(instance == null){
instance = new SingleTon();
}
return instance ;
}
}
在單線程的情況可以使用以上的代碼,但是遇到多線程就不行了,假設兩個線程同時進行到 if(instance==null) 然後我們就能得到兩個實例,顯然這是不對的,如果在面試中寫出這樣的代碼,簡直就是offer killer。
既然上述的代碼在多線程的情況下不能正常的工作,我就還需要對代碼進行處理,最簡單的方法就是加鎖:
</pre><pre name="code" class="java">public class SingleTon {
private SingleTon(){}
private static SingleTon instance ;
public static SingleTon getInstance(){
synchronized(SingleTon.class){
if(instance == null){
instance = new SingleTon();
}
}
return instance ;
}
}
在上面的代碼中我們看到加上同步關鍵字以後就能保證不會產生兩個對象了,但是新的問題來了,雖然這段代碼保證了正確性單同事也帶來了負面的影響,性能的下降,因爲在同一時刻只能有一個線程訪問,每次訪問的時候都會加鎖,加鎖是一個非常耗時的操作,在可能的情況下我們希望儘量減少加鎖的次數,
public class SingleTon {
private SingleTon(){}
private static SingleTon instance ;
public static SingleTon getInstance(){
if(instance == null){
synchronized(SingleTon.class){
if(instance == null){
instance = new SingleTon();
}
}
}
return instance ;
}
}
這樣只有在判斷到instance 爲 null 的時候纔對創建過程進行加鎖,這樣就能大大的減少加鎖的次數,這可以算是比較不錯了,但是我們還是希望有更好一些的解決方案,能不能不加鎖,很幸運,確實有,我們可以使用餓漢式來創建對象,
public class SingleTon {
private SingleTon(){}
private static SingleTon instance = new SingleTon() ;
public static SingleTon getInstance(){
return instance ;
}
}
這樣instance對象在類加載的時候就進行了初始化,而且能保證之後只有一個實例對象,而且不會出現併發下的異常與執行效率的問題,但是我們如果使用到的是該類的其他的靜態方法,而並不會使用類的任何實例對象,那麼這個對象依然會被創建,那我們能不能只在使用到該對象的時候進行對象的創建呢,答案是肯定的啦,
public class SingleTon {
private SingleTon(){}
public static SingleTon getInstance(){
return SingleTonHolder.instance ;
}
private static class SingleTonHolder{
private static SingleTon instance = new SingleTon();
}
}
It‘s over. 好啦,到這裏算是成功了。
public class SingleTon {
private static boolean hasInstance = false ;
private SingleTon(){
/*
*防止反射生成多對象
*/
synchronized (SingleTon.class) {
if(hasInstance) throw new RuntimeException("create multi obj is not allowed !") ;
hasInstance = true ;
}
}
public static SingleTon getInstance(){
return SingleTonHolder.getInstance() ;
}
/**
* 懶加載,線程安全
* @author jianwei.tan
*
*/
private static class SingleTonHolder{
private static final SingleTon instance = new SingleTon();
private static final SingleTon getInstance(){
return instance ;
}
}
/**
* 防止反序列化多實例
* @return
*/
private Object readResolve() {
return SingleTonHolder.instance ;
}
}