【Java】線程同步鎖和單例模式的多線程漏洞

在之前的例子中,多線程不做處理很容易出現線程安全問題,解決的方法就是“加鎖”。在Java中有兩種方式進行加鎖,一種是同步代碼塊,一種是同步函數。


同步代碼塊

將需要同步處理的代碼,用synchronized關鍵字進行包裝。比如之前的賣票例子,將if判斷封裝到synchronized關鍵字裏面。這樣,一旦某個線程進入synchronized代碼塊,在該線程退出之前,其它線程都無法進入。

public void sellTicket(){
    synchronized(obj){
        if(num>0){
            try {
                Thread.sleep(100);
                }catch (InterruptedException e){e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"....sale..."+num--);
        }
    }
}

同步代碼塊的鎖:synchronized關鍵字後面接的類對象就是“鎖”。多線程要保證鎖的一致性,確保使用的是同一個鎖。


同步函數

第二種方式是在需要同步的方法前面加synchronized關鍵字,叫作同步函數。

public synchronized void sellTicket(){
    if(num>0){
        try {
            Thread.sleep(100);
            }catch (InterruptedException e){e.printStackTrace();}
        System.out.println(Thread.currentThread().getName()+"....sale..."+num--);
    }
}

同步函數的鎖:同步代碼塊的鎖,是關鍵字後面的對象 。那同步函數的鎖是什麼呢?是調用同步函數的對象,一般是this指針。

靜態同步函數的鎖:如果方法是靜態的,那麼就沒有this指針。鎖是什麼呢?鎖就是該類的Class對象,可以通過getClass方法或者.class屬性進行獲取。


單例模式的多線程漏洞

單例模式的實現,有兩種方式,一種稱爲“餓漢式”,一種稱爲“懶漢式”。

//餓漢式
Class Singleton{
    private static final Singleton s=new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return s; 
    }
}

//懶漢式
Class Singleton{
    private static final Singleton s=null;
    private Singleton(){}
    public static Singleton getInstance(){
        if(s==null){
            s=new Singleton();        
        }
        return s; 
    }
}

查看有不有多線程漏洞,看①是否有共享數據?這裏共享數據就是單例對象s。②有無多線程操作?在多線程情況下,自然有。

問題:對於餓漢式而言,不存在多線程漏洞。但是對於懶漢式而言,就存在。

假設有兩個線程,線程0先進入if判斷,此時s==null,線程0進入if代碼塊,正要new一個對象的時候,CPU對線程進行切換。此時s仍舊是null,線程1也順利進入if代碼塊,new了一個Singleton對象。CPU再次切到線程0,線程0也new了一個Singleton對象。這時,就不是單例類了。

解決:加鎖不就行了嘛。但是這麼做,效率比較低,無論Singleton對象是否存在,每一次都要判斷鎖

public static synchronized Singleton getInstance(){
    if(s==null){
        s=new Singleton();
    }
    return s; 
}

再判斷鎖之前,加一個判斷,如果對象存在,就直接返回對象。如果對象不存在,再判斷鎖。可以減少鎖的判斷,提高效率。

public static Singleton getInstance(){
    if(s==null){
        synchronized(Singleton.class){
            if(s==null){
                 s=new Singleton();
            }
        }
    }
    return s; 
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章