Some people says if multiple threads are reading/writing then you need to use synchronized and if one thread is reading/writing and another one is only reading then you must use volatile. I don't get the difference between this situations.
There really isn't a hard and fast rule with this. Choosing whether or not to use synchronized
or volatile
has more to do with how the objects are being updated as opposed to how many readers or writers there are.
For example, you can achieve multiple readers and writers with an AtomicLong
which wraps a volatile long
.
private AtomicLong counter = new AtomicLong();
...
// many threads can get/set this counter without synchronized
counter.incrementAndGet();
And there are circumstances where you would need a synchronized
block even with a single reader/writer.
synchronized (status) {
status.setNumTransactions(dao.getNumTransactions());
// we don't want the reader thread to see `status` partially updated here
status.setTotalMoney(dao.getTotalMoney());
}
In the above example, since we are making multiple calls to update the status
object we may need to ensure that other threads don't see it when the num-transactions has been updated but not the total-money. Yes, AtomicReference
handles some of these cases but not all.
To be clear, marking a field volatile
ensures memory synchronization. When you read a volatile
field you cross a read memory barrier and when you write it you cross a write memory barrier. A synchronized
block has a read memory barrier at the start and a write barrier at the end of the block and is has mutex locking to ensure only one thread can enter the block at once.
Sometimes you just need memory barriers to achieve proper sharing of data between threads and sometimes you need locking.