我称“账户”一类
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
类是安全的调用该方法从两个线程在两个不同的对象。 但是你不能叫withDrawFromPrivateBalance()
在同一对象上从多个线程由于synchronized
关键字。 这是排序的相似。
您可以通过两种方式解决这个问题:要么使用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,然后只使用这个方法来改变字段的值。
标记同步的方法获得,该方法被上运行对象上的锁,但这里有两个不同的对象: ATMThread
和Account
。
在任何情况下,两个不同的ATMThread
s的使用不同的锁,所以他们写可以重叠,并与另一个冲突。
相反,你应该有两个ATMThread
S中的同一对象上同步。