[英]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類更安全。
標記方法同步獲得對正在運行該方法的對象的鎖定,但這里有兩個不同的對象: ATMThread
和Account
。
無論如何,兩個不同的ATMThread
使用不同的鎖,因此它們的寫入可以重疊並且彼此沖突。
相反,您應該讓兩個ATMThread
同步在同一個對象上。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.