2011-10-02 39 views
0

以下是非線程安全執行流行的丈夫妻子銀行賬戶問題。AtomicInteger不從主存儲器讀取值爲非易失性可變參考

(一個線程先檢查帳戶,並且在同一個線程執行撤銷之前,另一個線程執行撤銷操作,從而破壞代碼)。

如果我們在執行Demo.java文件後查看程序的日誌, 很明顯,「妻子線程」不是從主內存讀取AtomicInteger數額的值。

此外,我嘗試了與簡單的「volatile int」相同的示例。但同樣,我面臨同樣的問題: - 「妻子線程不讀取主存儲器中的整數值。」

請解釋這種行爲,以幫助我理解這個概念。 請找到下面的代碼: -

AtomicBankAccount.java

package pack; 

import java.util.concurrent.atomic.AtomicInteger; 

public class AtomicBankAccount { 

    private AtomicInteger amount ; 

    public AtomicBankAccount(int amt) { 
     this.amount = new AtomicInteger(amt) ; 
    } 

    // returns 
    // -1 for insufficient funds 
    // remaining balance without subtracting from actual amount for sufficient funds 
    public int check(int amtToWithdraw){ 

     if(amtToWithdraw <= amount.get()){ 
      System.out.println(Thread.currentThread().getName() + " checks amount : " + amount.get() + ". Remaining ammount after withdrawl should be : " + (amount.get() - amtToWithdraw)); 
      return (amount.get() - amtToWithdraw) ; 
     }else{ 
      return -1 ; 
     } 
    } 

    // returns 
    // remaining balance after subtracting from actual amount 
    public int withdraw(int amtToWithdraw){ 
     amount.getAndAdd(-amtToWithdraw) ; 
     System.out.println(Thread.currentThread().getName() + " withdraws " + amtToWithdraw + ". Remaining : " + amount.get() + " [latest updated value of account in main memory]"); 
     return amount.get() ; 
    } 

    public int getAmount(){ 
     return amount.get() ; 
    } 
} 

AtomicWithdrawThread.java

package pack; 

public class AtomicWithdrawThread extends Thread{ 

    private AtomicBankAccount account ; 

    public AtomicWithdrawThread(AtomicBankAccount acnt, String name) { 
     super(name) ; 
     this.account = acnt ; 
    } 

    @Override 
    public void run() { 
     int withDrawAmt = 2 ; 
     int remaining = 0 ; 
     while(true){ 

      if((remaining = account.check(withDrawAmt)) != -1){ 
       int temp = account.withdraw(withDrawAmt) ; 
       if(temp != remaining){ 
        System.out.println("[Race condition] " + Thread.currentThread().getName()); 
        System.exit(1) ; 
       } 
      }else{ 
       System.out.println("Empty Account...."); 
       System.exit(1) ; 
      } 
     } 
    } 
} 

Demo.java

package pack; 

public class Demo { 

    public static void main(String[] args) { 
     AtomicBankAccount bankAccount = new AtomicBankAccount(1000) ; 

     AtomicWithdrawThread husbandThread = new AtomicWithdrawThread(bankAccount, "husband") ; 
     AtomicWithdrawThread wifeThread = new AtomicWithdrawThread(bankAccount, "wife") ; 

     husbandThread.start() ; 
     wifeThread.start() ; 
    } 
} 

最好的問候,

RITS

回答

1

注:這些最初的幾個段落描述了這個問題故意缺乏線程安全的,並沒有真正回答提問者問的是點..

的檢查方法和退出方法,雖然單獨是原子操作,但不能組合成單個原子操作。

說丈夫檢查帳戶,發現有足夠的剩餘,然後被暫停。

妻子檢查賬戶,然後提取剩餘的錢。

丈夫然後被允許繼續,並試圖收回錢,但發現妻子已經全部消失。

編輯:描述了提問者的問題

你是不是在一個線程安全的方式調用的System.out的原因。在計算你要顯示的信息和實際讓它出現在控制檯上的信息之間存在競爭條件 - 所以妻子的信息可能在丈夫撤出之前計算出來,但是在其後顯示。

如果你想消除這種影響,你需要在這些System.out行(或者其他類似的東西)上添加同步關鍵字。

試想一下你的代碼實際上是這樣的:

String message = Thread.currentThread().getName() + " checks amount : " + amount.get() + ". Remaining ammount after withdrawl should be : " + (amount.get() - amtToWithdraw); 
System.out.println(message); 

這是否幫助顯示在競爭條件是什麼?

+0

我知道這種行爲,但問題是,妻子線程不能從主內存讀取ammount值。說,丈夫在旅途中留下了302盧比。在下一行日誌中說,妻子檢查了546盧比。 – mogli

+0

啊 - 你是否期望System.out是線程安全的並且不會受到競爭條件的影響? –

+0

是不是System.out線程安全? – mogli

1

該代碼看起來腥:

amount.getAndAdd(-amtToWithdraw) ; 
return amount.get() ; 

如果與其他線程毛骨悚然......有趣的事情都可能發生。使用和測試代碼,而不是(也在System.out請):

int amt = amount.getAndAdd(.amtToWithdraw); 
return amt - amtToWithdraw; 

在這裏也:

if(amtToWithdraw <= amount.get()){ 
     return (amount.get() - amtToWithdraw) ; 

再次使用該模式

int amt = amount.get(); 
    if(amtToWithdraw <= amt){ 
     return (amt - amtToWithdraw) ; 

但是,代碼is NOT fixable

 if((remaining = account.check(withDrawAmt)) != -1){ 
      int temp = account.withdraw(withDrawAmt) ; 

這些訪問到AtomicInteger其他線程可以在蠕變和沉船的Havok之間。您必須調整代碼以確保線程安全。

的通常模式/成語是這樣的:

// In AtomicBankAccount 
    public int withdraw(int amtToWithdraw){ 
     for(;;){ 
      int oldAmt = amount.get(); 
      int newAmt = oldAmt - amtToWithdraw; 
      if(newAmt < 0) 
       return -1; 
      if(amount.compareAndSet(oldAmt, newAmt)){ 
       System.out.println(Thread.currentThread().getName() + " withdraws " + amtToWithdraw + ". Remaining : " + newAmt + " [latest updated value of account in main memory]");  
       return newAmt; 
      } 
     } 
    } 

    // in AtomicWithdrawThread: 
    public void run() { 
     int withDrawAmt = 2 ; 
     while(true){ 
      if(account.withdraw(withDrawAmt) >= 0){ 
       // OK 
      } 
      else{ 
       System.out.println("Empty Account...."); 
       System.exit(1) ; 
      } 
     } 
    } 

注意checkWithdraw沒有更多。這很好,因爲沒有其他人可以在支票和實際取款之間得到。

相關問題