6

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:

My findings from different resources

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.

My summary

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.

Amin
  • 578
  • 1
  • 6
  • 18
  • 3
    You're just arguing with yourself here. The statement you made in the first sentence of your last paragraph is just a false assumption you've made, not a statement from a normative reference. [JLS 17.4](https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4) states 'a write to a volatile field happens-before every subsequent read of that field'. – user207421 Dec 20 '15 at 23:56
  • 2
    There's an even more persuasive JLS reference ... see by answer. – Stephen C Dec 21 '15 at 00:30
  • 1
    @StephenC Indeed, good catch. – user207421 Dec 21 '15 at 00:35
  • `nextSerialNumber++` is a sequence of two distinct operations on `nextSerialNumber` with at least one other operation in-between: It must fetch the original value from the variable to a _working location_, then it increments the working copy, and then it stores the result back into the variable. `this.someNumber = someNumber`, on the other hand, performs only _one_ operation on `this.someNumber`: It stores the new value. – Solomon Slow Dec 21 '15 at 14:08

2 Answers2

12

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.

janos
  • 120,954
  • 29
  • 226
  • 236
  • thanks for your response. So if I got it right, when writing to a volatile long or double by one thread, another thread can't just come and read in middle of writing until the writing is completely done. So it behaves something like synchronize on a method for instance. Is that right? – Amin Dec 21 '15 at 00:32
  • @moamzia It behaves like a volatile variable, which is exactly what it is. It would be completely useless if `volatile` allowed reading of Franken- values. The only problem here is your false assumption. – user207421 Dec 21 '15 at 00:47
  • 2
    I think this answer is extremely confusing and misleading. While it's true that without using `volatile` you might get word tearing, but you also might fail in other ways. It's misleading and confusing to suggest that word tearing is *the* problem. `volatile` doesn't just make sure word tearing doesn't happen, it provides specific guarantees against *any* possible failure mechanism, even on hardware that we know nothing whatsoever about. – David Schwartz Dec 21 '15 at 14:41
  • And operations like `x++` can be made atomic, it just so happens that `volatile` doesn't do that because `volatile` is not designed to provide atomicity, just visibility and ordering. It "happens to" provide atomicity in cases where atomicity is implied by visibility. But it *always* provides visibility, and this is what it's for. As worded, the answer suggests `volatile` provides atomicity "where possible", which isn't true. – David Schwartz Dec 21 '15 at 14:42
  • Nobody (apart from the OP, maybe) is suggesting that word tearing is *the problem*, or even an *important* problem. However, this Question is specifically asking about `long` and `double` ... and tearing. And it is true that without `volatile` (or some other form of synchronization) you can get word tearing. – Stephen C Dec 21 '15 at 15:01
  • Hi @DavidSchwartz, I'm trying to focus on the main question of the OP, which seems to be *"why using volatile on double or long, makes it atomic?"*. In any case I improved my explanation, I hope it's more clear now. – janos Dec 21 '15 at 15:38
  • @StephenC Well, that's the problem. The OP is definitely suggesting that. "*And* **that's why** *reading or writing to long or double variables are* **not atomic. Because** *it involves two operations ..*" And this answer doesn't do a good job of correcting that misunderstanding. – David Schwartz Dec 21 '15 at 15:55
  • @janos But the OP misunderstands why they're not atomic without `volatile` and you reinforce that misunderstanding in your first paragraph. – David Schwartz Dec 21 '15 at 15:56
  • If you ignored the first five words of the first sentence, you could come to that conclusion. Frankly, we cannot and should not tailor Answers to people who would do that. But feel free to post your own Answer. – Stephen C Dec 21 '15 at 22:45
8

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.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • 2
    @moamzia Specifically, *Effective Java* is not a normative reference. – user207421 Dec 21 '15 at 00:24
  • It is not even an Oracle document. – Stephen C Dec 21 '15 at 00:25
  • I'm sick of it myself, especially the way it gets treated as a pair of stone tablets from Mt Sinai. See [here](http://stackoverflow.com/questions/34377258/why-language-designers-allow-interface-to-contain-fields#comment56496287_34377258) for example. Talk about putting the cart before the horse. – user207421 Dec 21 '15 at 00:34
  • I believe the poster actually asked why volatile makes i++ atomic. The answer to that is that it won't since it is a compound statement. – sola Feb 03 '17 at 08:17
  • @sola - I disagree. Read his question again. He states that he understands that `i++` is non-atomic, whether or not `i` is `volatile`. – Stephen C Feb 03 '17 at 09:37