为什么不能按预期这种同步方法的工作?(why doesn't this synchroniz

2019-06-25 13:25发布

我称“账户”一类

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人能解释我为什么? 我使用的同步方法...

Answer 1:

这种方法是正确的,应当使用:

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;
    }
}

后者的解决方案显然不如前者,因为它很容易对外部同步某处忘记。 此外,你不应该使用公共字段。



Answer 2:

你的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,然后只使用这个方法来改变字段的值。



Answer 3:

标记同步的方法获得,该方法被上运行对象上的锁,但这里有两个不同的对象: ATMThreadAccount

在任何情况下,两个不同的ATMThread s的使用不同的锁,所以他们写可以重叠,并与另一个冲突。

相反,你应该有两个ATMThread S中的同一对象上同步。



文章来源: why doesn't this synchronized method work as expected?