Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 502
Hans-Peter Störr Avatar asked Jun 23 '10 15:06

Hans-Peter Störr


People also ask

Do I need volatile if I use synchronized?

Effectively, a variable declared volatile must have it's data synchronized across all threads, so that whenever you access or update the variable in any thread, all other threads immediately see the same value. Generally volatile variables have a higher access and update overhead than "plain" variables.


1 Answers

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.

like image 128
Mike Q Avatar answered Nov 10 '22 05:11

Mike Q