2014-11-25 88 views
0

考慮代碼:是否同步保持鎖定

class Account { 
    private int balance=50; 
    public int getBalance(){ 
    return balance; 
    } 
    public void withdraw(int amt){ 
     balance=balance-amt; 
     } 
    } 
public class AccountBalance implements Runnable { 

    private Account acc=new Account(); 

    public static void main(String[] args) { 
     AccountBalance accBal=new AccountBalance(); 
     Thread one=new Thread(accBal); 
     Thread two=new Thread(accBal); 
     one.setName("Thread One"); 
     two.setName("thread Two"); 
     one.start(); 
     two.start(); 
    } 
    public void run(){ 
     for(int i=0;i<5;i++){ 
      makeWithdrawal(10); 
     } 
    } 
    public synchronized void makeWithdrawal(int amount){ 
     if(acc.getBalance()>=amount){ 
      System.out.println(Thread.currentThread().getName()+" is going to withdraw "+acc.getBalance()); 
     try{ 
      Thread.sleep(500); 
     } 
     catch(InterruptedException ie){} 
     acc.withdraw(amount);    //line 1 
     System.out.println(Thread.currentThread().getName()+" completes the withdrawal"); 
     } 
     else { 
      System.out.println("Not enough in account for "+Thread.currentThread().getName()+" to withdraw "+acc.getBalance()); 
     } 
    } 
} 

1)假設主題弗雷德進入一個synchronized塊,並獲得對象鎖accBal然後在中間線程弗雷德呼叫另一種方法某處(第1行)這是不同步的,那麼線程不會釋放鎖定爲什麼?如果線程從synchronized同步出來,那麼它應該釋放鎖或者鎖直到synchronized塊。

2)假設我還有一個方法說changeAccount未然後同步的新線程說螺紋三其具有相同的對象accBal將進入該方法,並且可以改變可變平衡違反我們的同步代碼。

因此,即使存在非同步代碼並且線程在對象上有鎖,那麼另一個線程也不應該使用同一對象意味着一旦鎖定就進入非同步代碼已由Thread取得,除非釋放鎖,否則沒有Thread將作用於該對象。

回答

2
  1. 線程雖然執行另一個方法的代碼,但仍在執行原始的同步方法。這種其他方法的執行只是原始同步方法執行的一部分。

  2. 這是正確的,任何時間任何線程都可以更改未受保護的值。

  3. 還有其他的方法來進行相互排斥(如使用的CountDownLatch),但不完全同步的關鍵部分,你可以遇到這樣的過時的值多線程內存問題。

+1

CountDownLatch確實不提供互斥。事實上,它通常的用例基本上是相反的:它用來確保一組線程同時到達同一個點。 – 2014-11-25 14:39:16

+0

我不建議使用CountDownLatch作爲提供互斥的方式,但我在說它可能會以不太好的方式用於這樣做。 – NESPowerGlove 2014-11-25 15:43:18

0

1)假設線程佛瑞德進入同步塊和在中間螺紋佛瑞德獲得對象accBal鎖然後某處調用哪些是不同步的另一種方法(第1行),則該線程不釋放鎖定爲什麼?

考慮下面的代碼

synchronized (accBal) { 
    method() 
} 
// this line has to be reached before the lock on accBal is released 

當調用()的方法,它不退出同步塊,該同步塊是一個堆棧上「推」和保留用於呼叫時method()返回(退出)。因此,在調用method()期間不會釋放該鎖。

2)假設我有一個更多的方法說changeAccount這是不同步的,然後一個新的線程說線程其中三個是具有相同對象accBal將進入該方法,可以改變變量平衡,這違反了我們的同步碼。

正確。這會違反同步,並且行爲預測會有點棘手,並且線程之間的任何數據更改的可見性都容易受到數據競爭和優化下的副作用的影響。因此,重要的是每個人都尊重同步塊。

3)因此,即使存在非同步代碼並且線程對對象具有鎖定,Java也會提供任何內容,那麼另一個線程不應該使用同一對象輸入非同步代碼意味着一旦線程已經佔用一個鎖,除非釋放鎖,否則沒有線程將作用於該對象。

在Java中,所有悲觀的鎖定形式都要求每個線程都相互扮演好角色並檢查鎖定。其基本原理是執行併發檢查需要花費,並且大多數代碼不需要這些開銷。

0

不可以。如果您有訪問應該受保護的數據的方法,則應該同步該方法(或其中的關鍵代碼)(在同一對象上)。如果讓方法不同步,則意味着程序員認爲它訪問的數據不需要保護。

你可以有其他鎖定方法,但沒有什麼能夠使Java鎖定程序員不認爲值得鎖定的東西。鎖定會帶來性能損失,所以不受保護的代碼(程序員認爲是安全的)不應該被自動保護。

1
  1. 同步塊只在退出時釋放鎖。沒有什麼很好的理由爲什麼這種情況,如果你的邏輯能夠應付隨着你通過方法的進展而變化的成員變量,你就不會被它束縛。使用ReadWrite鎖(可能是ReentrantReadWriteLock)並研究它們提供的模式。
  2. 如果您有時使用鎖訪問成員變量,有時不使用,那麼您應該根本沒有它們。當然,像SONAR這樣的產品會用一個大紅色標誌來標記這種模式。同樣,您可能想要使用使用ReadWriteLock的細粒度方法。
+1

有一個很好的理由爲什麼'synchronized(lock){foo(); bar();}'不會放棄foo()調用中的鎖,然後在bar()調用中再次放棄它。如果foo()啓動暫時將某些數據結構置於其他線程不允許看到的狀態的操作,然後bar()完成該操作並再次使其安全?如果在兩次通話期間沒有辦法鎖定鎖,你怎麼能同步? – 2014-11-25 14:44:20