为什么这个同步方法不能按预期工作? [英] why doesn't this synchronized method work as expected?

查看:150
本文介绍了为什么这个同步方法不能按预期工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为帐户的课程

I have a class called "Account"

public class Account {

    public double balance = 1500;

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

以及一个名为ATMThread的课程

and a class called 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美元)

I create two threads, we assume there is an initial balance in the bank account(1500$)

第一个线程试图提取100美元和第二个线程。

the first thread tries to withdraw 100$ and the second thread as well.

我预计最终余额为1300,但有时为1400.有人可以解释一下原因吗?我正在使用同步方法...

I expect the final balance to be 1300, however it is sometimes 1400. Can someone explain me why? I'm using synchronized methods...

推荐答案

此方法是正确的,应该使用:

This method is correct and should be used:

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

它一次只能将对帐户内部状态的访问限制为一个线程。但是你的余额字段是 public (所以不是真正的内部),这是所有问题的根本原因:

It correctly restricts access to the account internal state to only one thread at a time. However your balance field is public (so not really internal), which is the root cause of all your problems:

public double balance = 1500;

利用您正在访问的 public 修饰符它来自两个主题:

Taking advantage of public modifier you are accessing it from two threads:

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

此方法,即使看起来正确 synchronized 关键字,它不是。您正在创建两个线程并且 synchronized 线程基本上是一个绑定到对象的锁。这意味着这两个线程具有单独的锁,每个都可以访问自己的锁。

This method, even though looks correct with synchronized keyword, it is not. You are creating two threads and synchronized thread is basically a lock tied to an object. This means these two threads have separate locks and each can access its own lock.

想想你的 withDrawFromPrivateBalance()方法。如果您有两个 Account 类的实例,则可以安全地从两个不同对象上的两个线程调用该方法。但是,由于 synchronized 关键字,您无法在多个线程的同一对象上调用 withDrawFromPrivateBalance()。这有点类似。

Think about your withDrawFromPrivateBalance() method. If you have two instances of Account class it is safe to call that method from two threads on two different objects. However you cannot call withDrawFromPrivateBalance() on the same object from more than one thread due to synchronized keyword. This is sort-of similar.

您可以通过两种方式修复它:直接使用 withDrawFromPrivateBalance()(请注意,此处不再需要 synchronized

You can fix it in two ways: either use withDrawFromPrivateBalance() directly (note that synchronized is no longer needed here):

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

或锁定两个线程中的同一个对象,而不是锁定两个独立的线程对象实例:

or lock on the same object in both threads as opposed to locking on two independent Thread object instances:

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

后一种解决方案明显不如前一种解决方案,因为在某处很容易忘记外部同步。你也不应该使用公共字段。

The latter solution is obviously inferior to the former one because it is easy to forget about external synchronization somewhere. Also you should never use public fields.

这篇关于为什么这个同步方法不能按预期工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆