單例模式在多線程下的多種實現模式


單例模式是23種設計模式中比較常見的設計模式,又因為其代碼量精簡,所以經常會被用在在面試中測試面試者的能力。

初級的單例模式很簡單

實現兩個要求

1構造方法私有化

2對外提供靜態的,公開的獲取對象的方法

所以:初級單例模式如下

public class Singelton {
private Singelton(){}
private static Singelton sin=null;
public static Singelton getSingelton(){
           if(sin==null){
               sin=new Singelton();
          }
return sin;
}

}

----------------------------------------

但是這樣就夠了嗎?

隨着學習的深入,我們知道程序大多數在多線程的環境下運行。這就對我們的代碼提出了更高質量的要求,要求我們考慮線程安全問題。

僅僅是上面的那段代碼無法保證線程的安全。於是:

public class SingletonThread {
private SingletonThread() {}
private static SingletonThread st=null;
public synchronized static SingletonThread getSingletonThread(){
if(st==null){
st=new SingletonThread();
}
return st;
}

}

這段代碼考慮到了線程安全,但是,在方法上加鎖代價是否太大了?效率與單線程相近,假設這個方法中有上萬行代碼,在方法上加鎖

是很不划算的。

 

所以,我們有更好的方法

1

public final class SingletonOne {//餓漢式,不能實現按需分配
private SingletonOne(){};
private static SingletonOne sin=new SingletonOne();
public static SingletonOne getSingleton(){
return sin;
}
}

利用靜態成員僅在類加載階段執行一次的性質,得到唯一的對象。

此方法不僅線程安全,而且方法簡介。

 

2我們能否不一開始就創建類的實例呢?做到按需分配

如下:

public final class SingletonTwo {
private SingletonTwo(){};
public static SingletonTwo setsin(){
return singleton.sin;
}
static class singleton{//內部類不會再外部類加載時加載,故此是按需分配。
private singleton() {};
private static final SingletonTwo sin=new SingletonTwo();

}
}

利用內部類不會在外部類加載時被加載的性質,真正實現了按需分配。

---------------------------------------------

以上兩種方法是極好的,但是也需要根據實際情況使用,因為類中的方法和屬性都是靜態的,即使被繼承之后也會被隱藏,

不能通過重寫來實現多態,已經失去了被繼承的意義,故此還有另一種推薦方法:

3

public class SingletonV {
private SingletonV(){}
private volatile SingletonV singleton=null;
public SingletonV getSingleton(){
if(singleton==null){
synchronized (SingletonV.class) {
if(singleton==null){
singleton=new SingletonV();
}
}
}
return singleton;
}
}

這樣類還保留了繼承的意義,同樣要加鎖,但是開銷小得多。利用了關鍵字volatile。

具體用法如下

//java內存模型規定所有的變量都是存在主存當中(類似於前面說的物理內存),
//每個線程都有自己的工作內存(類似於前面的高速緩存)。
//線程對變量的所有操作都必須在工作內存中進行,
//而不能直接對主存進行操作。並且每個線程不能訪問其他線程的工作內存。
//這就可能造成一個線程在主存中修改了一個變量的值,
//而另外一個線程還繼續使用它在自己工作內存中的變量值的拷貝,造成數據的不一致。
//要解決這個問題,把該變量聲明為volatile(不穩定的)即可,
//這就指示JVM,這個變量是不穩定的, 每次使用它都到 主存中 進行讀取。
//一般說來,多任務環境下各任務間共享的標志都應該加volatile修飾。
//Volatile修飾的成員變量在每次被線程訪問時, 都強迫 從共享內存中 重讀 該成員變量的值。
//而且,當成員變量發生變化時,強迫 線程將變化值 回寫到共享內存。這樣在任何時刻,
//兩個不同的線程總是看到某個成員變量的同一個值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM