一次同步方法的失敗與原因總結(jié)

synchronized關(guān)鍵字可以設(shè)置同步方法和同步塊,同步方法會對調(diào)用對象this上鎖,
所有線程進入前都需要獲取這個鎖,這里我拿一個錯誤樣例來做示范

public class Test {
    public static void main(String[] args) {
        Account account=new Account(100);
        Person p1=new Person(account,80);
        Person p2=new Person(account,90);
        p1.start();
        p2.start();
    }   
}

class Account{
    int total;  
    public Account(int total) {
        super();
        this.total=total;       
    }   
}

class Person extends Thread{
    private int reduce;
    private Account account;
    public Person(Account account,int reduce) {
    
        this.account=account;
        this.reduce=reduce;
    }
    
    public synchronized void run() {    
        if (account.total-reduce<0) return ;
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        account.total-=reduce;
        System.out.println(Thread.currentThread().getName()+"取出"+reduce);
        System.out.println(Thread.currentThread().getName()+"剩余"+account.total);
        
    }
}

Thread-0取出80
Thread-0剩余-70
Thread-1取出90
Thread-1剩余-70

出現(xiàn)了負(fù)數(shù),很明顯沒鎖住,現(xiàn)在我們來分析下原因。
有1個account對象,兩個person對象p1和p2,p1和p2爭搶資源account
我一開始的設(shè)想是,當(dāng)p1進入方法時,p1對象上鎖,account作為p1的成員也被上鎖,此時p2進入就需要等到p1釋放鎖,由此達(dá)到我們的目標(biāo)。

問題在于:java規(guī)定每個對象都有一個監(jiān)視器,使用時查看上鎖了沒,上面的代碼中雖然對person對象上了鎖,同時account作為對象也有監(jiān)視器,account雖然作為person的成員,但account的監(jiān)視器是單獨的,不受p1的鎖影響?,F(xiàn)在我們再來場景復(fù)原一下,p1先進入方法,對p1上鎖,計算100-80=20,同時p2進入方法,查看account的監(jiān)視器,沒有上鎖,計算20-90=-70,這就產(chǎn)生了錯誤。

解決方法就是使用同步塊給account上鎖

public void run() { 
        synchronized(account) {
        if (account.total-reduce<0) return ;
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        account.total-=reduce;
        System.out.println(Thread.currentThread().getName()+"取出"+reduce);
        System.out.println(Thread.currentThread().getName()+"剩余"+account.total);
        
        }
    }

這個問題給我們的教訓(xùn)在于,每個對象都有單獨的監(jiān)視器,要選取正確的爭奪對象上鎖,基于此同步塊不僅能縮小鎖的粒度還能選取正確的對象上鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容