1. 程式人生 > >單例模式涉及到的執行緒安全問題

單例模式涉及到的執行緒安全問題

1.單例模式

單例模式分為餓漢式和懶漢式,也即 即時載入和懶載入的分別
下面分別看兩個形式:

1.1餓漢式

class SingleHungry{
    //1.首先私有化構造器,這樣使該類不可以被其他物件建立物件
    //2.自己內部建立物件
    //3.寫一個靜態方法讓外部呼叫例項
    private static Single s = new Single();
    //上面一句因為getInstance()是靜態方法,所以s為靜態變數
    private Single(){}
    public static Single getInstance(){
        return
s; } }

1.2懶漢式

class SingleLazy{
    //1.首先私有化構造器,這樣使該類不可以被其他物件建立物件
    //2.自己內部建立物件
    //3.寫一個靜態方法讓外部呼叫例項
    //4.這裡有個注意點就是:懶漢式有著對例項是否為空的判斷
    private static Single s = null;
    //上面一句因為getInstance()是靜態方法,所以s為靜態變數
    private Single(){}
    public static Single getInstance(){
        if(s == null){
            s = new
Single(); } return s; } }

1.這裡我們可以看到,餓漢式與懶漢式的區別就在於:餓漢式類載入就建立了物件,而懶漢式則不是,懶漢式通過對物件是否為空的判斷進行,如果物件為空則建立物件,不為空直接返回物件
2.我們考慮一下兩個單例模式,餓漢式只有一句程式碼 return s所以餓漢式不存在多執行緒安全問題,懶漢式有兩句程式碼,並且有一句判斷條件,這樣就存線上程安全問題,產生原因可以看另一篇文章 http://blog.csdn.net/cronousgt/article/details/78418590,這裡我們可以使用同步函式來解決這個問題,程式碼如下:

class SingleLazy{
    //1.首先私有化構造器,這樣使該類不可以被其他物件建立物件
    //2.自己內部建立物件
    //3.寫一個靜態方法讓外部呼叫例項
    //4.這裡有個注意點就是:懶漢式有著對例項是否為空的判斷
    private static Single s = null;
    //上面一句因為getInstance()是靜態方法,所以s為靜態變數
    private Single(){}
    public synchronized static Single getInstance(){
        if(s == null){
            s = new Single();
        }
        return s;
    }
}

這裡的靜態函式鎖為SingleLazy.class物件,但是又存在一個問題,每個物件進來都判斷鎖,實際上一定程度上影響了效率,我們想想怎麼改進,看程式碼:

class SingleLazy{
    //1.首先私有化構造器,這樣使該類不可以被其他物件建立物件
    //2.自己內部建立物件
    //3.寫一個靜態方法讓外部呼叫例項
    //4.這裡有個注意點就是:懶漢式有著對例項是否為空的判斷
    private static Single s = null;
    //上面一句因為getInstance()是靜態方法,所以s為靜態變數
    private Single(){}
    public  static Single getInstance(){
        if(s == null){  //提前判斷例項物件是否為空,為空在進行獲取例項物件
            synchronized(Single.class){
                if(s == null){
                    s = new Single();
                }
            }
        }
        return s;
    }
}

這裡我們採用了同步程式碼塊,加上if判斷語句
原理:假如執行緒1先判斷有沒有物件,沒有就進同步程式碼塊建立物件,這時第二個執行緒也來了,他判斷也沒有例項物件,也進同步程式碼塊,但是這時候它判斷執行緒1正在持有鎖,所以2進不去,當執行緒1建立物件並釋放鎖之後,執行緒2得到鎖,進入同步程式碼塊,但是這時發現s不等於null,函式就直接返回物件給執行緒二了,之後的執行緒都是可以直接拿到物件了。
這樣只要建立了物件其它執行緒進來不必一直判斷鎖,提高了效率