1. 程式人生 > >一次同步方法的失敗與原因總結

一次同步方法的失敗與原因總結

synchronized關鍵字可以設定同步方法和同步塊,同步方法會對呼叫物件this上鎖,
所有執行緒進入前都需要獲取這個鎖,這裡我拿一個錯誤樣例來做示範

 1 public class Test {
 2     public static void main(String[] args) {
 3         Account account=new Account(100);
 4         Person p1=new Person(account,80);
 5         Person p2=new Person(account,90);
 6         p1.start();
7 p2.start(); 8 } 9 } 10 11 class Account{ 12 int total; 13 public Account(int total) { 14 super(); 15 this.total=total; 16 } 17 } 18 19 class Person extends Thread{ 20 private int reduce; 21 private Account account; 22 public
Person(Account account,int reduce) { 23 24 this.account=account; 25 this.reduce=reduce; 26 } 27 28 public synchronized void run() { 29 if (account.total-reduce<0) return ; 30 try { 31 Thread.sleep(200); 32 } catch (InterruptedException e) {
33 // TODO Auto-generated catch block 34 e.printStackTrace(); 35 } 36 account.total-=reduce; 37 System.out.println(Thread.currentThread().getName()+"取出"+reduce); 38 System.out.println(Thread.currentThread().getName()+"剩餘"+account.total); 39 40 } 41 }

 


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

出現了負數,很明顯沒鎖住,現在我們來分析下原因。
有1個account物件,兩個person物件p1和p2,p1和p2爭搶資源account
我一開始的設想是,當p1進入方法時,p1物件上鎖,account作為p1的成員也被上鎖,此時p2進入就需要等到p1釋放鎖,由此達到我們的目標。

問題在於:java規定每個物件都有一個監視器,使用時檢視上鎖了沒,上面的程式碼中雖然對person物件上了鎖,同時account作為物件也有監視器,account雖然作為person的成員,但account的監視器是單獨的,不受p1的鎖影響。現在我們再來場景復原一下,p1先進入方法,對p1上鎖,計算100-80=20,同時p2進入方法,檢視account的監視器,沒有上鎖,計算20-90=-70,這就產生了錯誤。

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

 1 public void run() {    
 2         synchronized(account) {
 3         if (account.total-reduce<0) return ;
 4         try {
 5             Thread.sleep(200);
 6         } catch (InterruptedException e) {
 7             // TODO Auto-generated catch block
 8             e.printStackTrace();
 9         }
10         account.total-=reduce;
11         System.out.println(Thread.currentThread().getName()+"取出"+reduce);
12         System.out.println(Thread.currentThread().getName()+"剩餘"+account.total);
13         
14         }
15     }

 


這個問題給我們的教訓在於,每個物件都有單獨的監視器,要選取正確的爭奪物件上鎖,基於此同步塊不僅能縮小鎖的粒度還能選取正確的物件上鎖。