Search code examples
javaconcurrencysynchronizationvolatilesynchronized

Difference between synchronization of field reads and volatile


In a nice article with some concurrency tips, an example was optimized to the following lines:

double getBalance() {
    Account acct = verify(name, password);
    synchronized(acct) { return acct.balance; }
}

If I understand that correctly, the point of the synchronization is to ensure that the value of acct.balance that are read by this thread is current and that any pending writes to the fields of the object in acct.balance are also written to main memory.

The example made me think a little: wouldn't it be more efficient to just declare acct.balance (i.e. the field balance of class Account) as volatile? It should be more efficient, save you all the synchronize on accesses to acct.balance and would not lock the whole acct object. Am I missing something?


Solution

  • You are correct. volatile provides a visibility guarantee. synchronized provides both a visibility guarantee AND serialisation of protected code sections. For VERY simple situations volatile is enough, however it is easy to get into trouble using volatile instead of synchronisation.

    If you were to assume that Account has a way of adjusting its balance then volatile is not good enough

    public void add(double amount)
    {
       balance = balance + amount;
    }
    

    Then we have a problem if balance is volatile with no other synchronization. If two threads were to try and call add() together you could have a "missed" update where the following happens

    Thread1 - Calls add(100)
    Thread2 - Calls add(200)
    Thread1 - Read balance (0)
    Thread2 - Read balance (0)
    Thread1 - Compute new balance (0+100=100)
    Thread2 - Compute new balance (0+200=200)
    Thread1 - Write balance = 100
    Thread2 - Write balance = 200 (WRONG!)
    

    Obviously this is wrong because both threads read the current value and updated independently and then wrote it back (read, compute, write). volatile does not help here so you would need synchronized to ensure one thread completed the entire update before the other thread began.

    I general find that if when writing some code I think "can I use volatile instead of synchronized" the answer might well be "yes" but the time/effort of figuring it out for sure and the danger of getting it wrong is not worth the benefit (minor performance).

    As an aside a well written Account class would handle all the synch logic internally so callers don't have to worry about it.