簡體   English   中英

為什么這個同步方法不能按預期工作?

[英]why doesn't this synchronized method work as expected?

我有一個名為“帳戶”的班級

public class Account {

    public double balance = 1500;

    public synchronized double withDrawFromPrivateBalance(double a) {
        balance -= a;
        return balance;
    }
}

還有一個名為ATMThread的類

public class ATMThread extends Thread {
    double localBalance = 0;
    Account myTargetAccount;

    public ATMThread(Account a) {
        this.myTargetAccount = a;
    }

    public void run() {
        find();
    }

    private synchronized void find() {
        localBalance = myTargetAccount.balance;
        System.out.println(getName() + ": local balance = " + localBalance);
        localBalance -= 100;
        myTargetAccount.balance =  localBalance;
    }

    public static void main(String[] args) {
        Account account = new Account();
        System.out.println("START: Account balance = " + account.balance);

        ATMThread a = new ATMThread(account);
        ATMThread b = new ATMThread(account);

        a.start();
        b.start();

        try {
            a.join();
            b.join();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }

        System.out.println("END: Account balance = " + account.balance);
    }

}

我創建了兩個線程,我們假設銀行賬戶中有一個初始余額(1500 $)

第一個線程嘗試撤銷100 $和第二個線程。

我希望最終余額為1300,但有時候是1400.有人可以解釋一下為什么嗎? 我正在使用同步方法......

此方法是正確的,應該使用:

public synchronized double withDrawFromPrivateBalance(double a)
{
          balance -= a;
          return balance;
}

它正確地將對帳戶內部狀態的訪問限制為一次只有一個線程。 但是,您的balance字段是public (因此不是內部的),這是所有問題的根本原因:

public double balance = 1500;

利用public修飾符,您可以從兩個線程訪問它:

private synchronized void find(){
    localBalance = myTargetAccount.balance;
    System.out.println(getName() + ": local balance = " + localBalance);
    localBalance -= 100;
    myTargetAccount.balance =  localBalance;
}

即使使用synchronized關鍵字看起來正確,這種方法也不是。 您正在創建兩個線程, synchronized線程基本上是一個綁定到對象的鎖。 這意味着這兩個線程具有單獨的鎖,每個都可以訪問自己的鎖。

想想你的withDrawFromPrivateBalance()方法。 如果您有兩個Account類實例,則可以安全地從兩個不同對象上的兩個線程調用該方法。 但是,由於synchronized關鍵字,您無法在多個線程的同一對象上調用withDrawFromPrivateBalance() 這有點類似。

您可以通過兩種方式修復它:直接使用withDrawFromPrivateBalance() (請注意,此處不再需要synchronized ):

private void find(){
    myTargetAccount.withDrawFromPrivateBalance(100);
}

或鎖定兩個線程中的同一對象,而不是鎖定兩個獨立的Thread對象實例:

private void find(){
    synchronized(myTargetAccount) {
      localBalance = myTargetAccount.balance;
      System.out.println(getName() + ": local balance = " + localBalance);
      localBalance -= 100;
      myTargetAccount.balance =  localBalance;
    }
}

后一種解決方案明顯不如前者,因為很容易忘記某處的外部同步。 你也不應該使用公共領域。

您的private synchronized void find()方法正在同步不同的鎖。 嘗試在相同的對象上同步它

private void find(){
    synchronized(myTargetAccount){
        localBalance = myTargetAccount.balance;
        System.out.println(getName() + ": local balance = " + localBalance);
        localBalance -= 100;
        myTargetAccount.balance =  localBalance;
    }
}

您還可以通過將其字段設為私有並將synchronized修飾符添加到其所有 getter和setter,然后僅使用此方法更改字段值來使您的Account類更安全。

標記方法同步獲得對正在運行該方法的對象的鎖定,但這里有兩個不同的對象: ATMThreadAccount

無論如何,兩個不同的ATMThread使用不同的鎖,因此它們的寫入可以重疊並且彼此沖突。

相反,您應該讓兩個ATMThread同步在同一個對象上。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM