Volatile Vs Atomic [duplicate]
I read somewhere below line.
Java volatile keyword doesn't means atomic, its common misconception that after declaring volatile,
++
operation will be atomic, to make the operation atomic you still need to ensure exclusive access usingsynchronized
method or block in Java.
So what will happen if two threads attack a volatile
primitive variable at same time?
Does this mean that whosoever takes lock on it, that will be setting its value first. And if in meantime, some other thread comes up and read old value while first thread was changing its value, then doesn't new thread will read its old value?
What is the difference between Atomic and volatile keyword?
The effect of the volatile
keyword is approximately that each individual read or write operation on that variable is made atomically visible to all threads.
Notably, however, an operation that requires more than one read/write -- such as i++
, which is equivalent to i = i + 1
, which does one read and one write -- is not atomic, since another thread may write to i
between the read and the write.
The Atomic
classes, like AtomicInteger
and AtomicReference
, provide a wider variety of operations atomically, specifically including increment for AtomicInteger
.
Volatile and Atomic are two different concepts. Volatile ensures, that a certain, expected (memory) state is true across different threads, while Atomics ensure that operation on variables are performed atomically.
Take the following example of two threads in Java:
Thread A:
value = 1;
done = true;
Thread B:
if (done)
System.out.println(value);
Starting with value = 0
and done = false
the rule of threading tells us, that it is undefined whether or not Thread B will print value. Furthermore value is undefined at that point as well! To explain this you need to know a bit about Java memory management (which can be complex), in short: Threads may create local copies of variables, and the JVM can reorder code to optimize it, therefore there is no guarantee that the above code is run in exactly that order. Setting done to true and then setting value to 1 could be a possible outcome of the JIT optimizations.
volatile
only ensures, that at the moment of access of such a variable, the new value will be immediately visible to all other threads and the order of execution ensures, that the code is at the state you would expect it to be. So in case of the code above, defining done
as volatile will ensure that whenever Thread B checks the variable, it is either false, or true, and if it is true, then value
has been set to 1 as well.
As a side-effect of volatile, the value of such a variable is set thread-wide atomically (at a very minor cost of execution speed). This is however only important on 32-bit systems that i.E. use long (64-bit) variables (or similar), in most other cases setting/reading a variable is atomic anyways. But there is an important difference between an atomic access and an atomic operation. Volatile only ensures that the access is atomically, while Atomics ensure that the operation is atomically.
Take the following example:
i = i + 1;
No matter how you define i, a different Thread reading the value just when the above line is executed might get i, or i + 1, because the operation is not atomically. If the other thread sets i to a different value, in worst case i could be set back to whatever it was before by thread A, because it was just in the middle of calculating i + 1 based on the old value, and then set i again to that old value + 1. Explanation:
Assume i = 0
Thread A reads i, calculates i+1, which is 1
Thread B sets i to 1000 and returns
Thread A now sets i to the result of the operation, which is i = 1
Atomics like AtomicInteger ensure, that such operations happen atomically. So the above issue cannot happen, i would either be 1000 or 1001 once both threads are finished.
There are two important concepts in multithreading environment:
- atomicity
- visibility
The volatile
keyword eradicates visibility problems, but it does not deal with atomicity. volatile
will prevent the compiler from reordering instructions which involve a write and a subsequent read of a volatile variable; e.g. k++
.
Here, k++
is not a single machine instruction, but three:
- copy the value to a register;
- increment the value;
- place it back.
So, even if you declare a variable as volatile
, this will not make this operation atomic; this means another thread can see a intermediate result which is a stale or unwanted value for the other thread.
On the other hand, AtomicInteger
, AtomicReference
are based on the Compare and swap instruction. CAS has three operands: a memory location V
on which to operate, the expected old value A
, and the new value B
. CAS
atomically updates V
to the new value B
, but only if the value in V
matches the expected old value A
; otherwise, it does nothing. In either case, it returns the value currently in V
. The compareAndSet()
methods of AtomicInteger
and AtomicReference
take advantage of this functionality, if it is supported by the underlying processor; if it is not, then the JVM implements it via spin lock.
As Trying as indicated, volatile
deals only with visibility.
Consider this snippet in a concurrent environment:
boolean isStopped = false;
:
:
while (!isStopped) {
// do some kind of work
}
The idea here is that some thread could change the value of isStopped
from false to true in order to indicate to the subsequent loop that it is time to stop looping.
Intuitively, there is no problem. Logically if another thread makes isStopped
equal to true, then the loop must terminate. The reality is that the loop will likely never terminate even if another thread makes isStopped
equal to true.
The reason for this is not intuitive, but consider that modern processors have multiple cores and that each core has multiple registers and multiple levels of cache memory that are not accessible to other processors. In other words, values that are cached in one processor's local memory are not visisble to threads executing on a different processor. Herein lies one of the central problems with concurrency: visibility.
The Java Memory Model makes no guarantees whatsoever about when changes that are made to a variable in one thread may become visible to other threads. In order to guarantee that updates are visisble as soon as they are made, you must synchronize.
The volatile
keyword is a weak form of synchronization. While it does nothing for mutual exclusion or atomicity, it does provide a guarantee that changes made to a variable in one thread will become visible to other threads as soon as it is made. Because individual reads and writes to variables that are not 8-bytes are atomic in Java, declaring variables volatile
provides an easy mechanism for providing visibility in situations where there are no other atomicity or mutual exclusion requirements.