I'm trying to learn the terminologies used in multi-threading in Java. So please correct me if I used wrong definition in following text:
Atomic action: According to Java doc:
In programming, an atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete.
And that's why reading or writing to long or double variables are not atomic. Because it involves two operations, first 32-bit and the second-32 bit read/write to the variable. Also, from the paragraph above, I understand that if I used synchronized
on a method, it will make the method atomic (theoretically speaking).
Volatile variables: Also from Java Doc:
This means that changes to a volatile variable are always visible to other threads. What's more, it also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.
Now, also according to Effective Java 2nd Edition by Joshua Bloch, consider the following points mentioned in the book about volatile
declaration:
Consider the following:
// Broken - requires synchronization!
private static volatile int nextSerialNumber = 0;
public static int generateSerialNumber() {
return nextSerialNumber++;
}
The method’s state consists of a single atomically accessible field, nextSerialNumber, and all possible values of this field are legal. Therefore, no synchronization is necessary to protect its invariants. Still, the method won’t work properly without synchronization.
This is because nextSerialNumber++
is not atomic as it performs read, increment, write.
So if nextSerialNumber++
is not atomic, and requires synchronize
. Then why the following is atomic and doesn't require synchronize?
private static volatile long someNumber = 0;
public static int setNumber(long someNumber) {
return this.someNumber = someNumber;
}
What I don't understand is why using volatile on double
or long
, makes it atomic?
Because all volatile
does is that it makes sure if thread B tried to read a long
variable that is being written by thread A, and only 32-bit of it is written by thread A, then when thread B accesses the resource, it would get the 32-bit number that was written by thread A. And that doesn't make it atomic as the definition of the term atomic is explained in Java Doc.
It's not atomic because it's a multiple-step operation at the machine code level. That is, longs and doubles are longer than the processor's word length.
The volatile keyword is used: to make non atomic 64-bit operations atomic: long and double . (all other, primitive accesses are already guaranteed to be atomic!)
int or long volatiles This means that while your main code section (e.g. your loop) reads the first 8 bits of the variable, the interrupt might already change the second 8 bits. This will produce random values for the variable.
What I don't understand is why using volatile on double or long, makes it atomic?
Without using the volatile
keyword, you might read the first 32 bits of a double
or long
written by one thread, and the other 32 bits written by another thread, called word tearing, and clearly not atomic.
The volatile
keyword makes sure that cannot happen. The 64 bit value you read will be a value written by one thread, not some Franken-value that is the result of writes by multiple threads. This is what it means that these types become atomic thanks to the volatile
keyword.
The volatile
keyword cannot make an operation like x++
atomic, regardless of the type (64 bit or 32 bit), because it's a compound operation (read + increment + write), as opposed to a simple write. The operations involved in a x++
may be interleaved by operations by other threads. The volatile
keyword cannot make such compound operations atomic.
So if
nextSerialNumber++
is not atomic, and requires synchronize. Then why the following is atomic and doesn't require synchronize?private static volatile long someNumber = 0; public static int setNumber(long someNumber) { return this.someNumber = someNumber; }
nextSerialNumber++
requires synchronization because it's a compound operation, and therefore not atomic.
this.someNumber = someNumber
is atomic thanks to this.someNumber
being volatile
, and an assignment operation is also atomic, being a single operation. Therefore there is no need to synchronize. Without volatile
, this.someNumber
could not be written in an atomic way, so synchronization would be necessary.
What I don't understand is why using volatile on double or long, makes it atomic?
Here's why. Using volatile
with a double
or long
makes them atomic because the JLS says so.
The JLS (Section 17.7) states that:
"Writes and reads of volatile long and double values are always atomic."
Note: the JLS is normative. The javadocs are non-normative as far as the language semantics are concerned. Bloch's "Effective Java" is not even a Oracle document - it is merely an (unauthorized) commentary.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With