why doesn't this synchronized method work as expected?

后端 未结 3 914
一向
一向 2020-12-20 17:45

I have a class called \"Account\"

public class Account {

    public double balance = 1500;

    public synchronized double withDrawFromPrivateBalance(double         


        
相关标签:
3条回答
  • 2020-12-20 18:20

    Your private synchronized void find() method is synchronizing on different locks. Try synchronizing it on same objects like

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

    You can also make your Account class more thread safe, by making its fields private and adding synchronized modifier to all its getters and setters and then use only this methods to change value of fields.

    0 讨论(0)
  • 2020-12-20 18:23

    Marking a method synchronized obtains a lock on the object that the method is being run on, but there are two different objects here: the ATMThread and the Account.

    In any event, the two different ATMThreads are using different locks, so their writes can overlap and conflict with one another.

    Instead, you should have both ATMThreads synchronize on the same object.

    0 讨论(0)
  • 2020-12-20 18:25

    This method is correct and should be used:

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

    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;
    

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

    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.

    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.

    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.

    0 讨论(0)
提交回复
热议问题