2

I'm reading JCIP and am having trouble understand the following statement in 3.3.1,

It is safe to perform read-modify-write operations on shared volatile variables as long as you can ensure that the volatile variable is only written from a SINGLE thread. In this case you are confining the modifications to a single thread to prevent race conditions, and the visibility guarantees for volatile variables ensures that other threads see the most up-to-date value.

Even if the volatile variable is only written from a single thread, how can it not raise race conditions? If thread A performs, say, counter++ when counter is 1, thread B could get in just before counter+1 is written back to memory and get a stale value 1 of counter. How is that ensuring "prevent race conditions" and "other threads see the most up-to-date value"??

p.s. I know there's a same question here, but I didn't find any satisfying answer.

  • 1
    "thread B could get in just before counter+1 is written back to memory". Yes - and that means that Thread B gets the most up-to-date value that was there when it read the data. That's what it ensures. How is this creating a race condition in your opinion? – Erwin Bolwidt Dec 19 '17 at 03:18
  • 1
    The spec is as clear as it can be. In your example all threads including thread B will see 1 before `counter+1` is written to memory. Once it is written they all will see 2. In no way will some threads see 1 and others 2. – tsolakp Dec 19 '17 at 03:23
  • @tsolakp You're right. I was confused about the concept of "stale values". Here thread B reads the "updated" value, not the "stale" value, because that's what is last written to memory by A. And threads A and B are perfectly synchronized here. – justgivememyicecream Dec 19 '17 at 05:09
  • @Erwin It makes sense now after reading what you said. Thanks. – justgivememyicecream Dec 19 '17 at 05:21

1 Answers1

4

How can read-modify-write operations of volatile variables be thread safe

In general, they can't.

The text that you quoted says:

"It is safe to perform read-modify-write operations on shared volatile variables as long as you can ensure that the volatile variable is only written from a SINGLE thread."

That is NOT what thread safe means. As a general statement.

Even if the volatile variable is only written from a single thread, how can it not raise race conditions?

Well, there are no race conditions for the reading threads because they will only see the most recently written value of the variable. And the writing thread is governed by normal execution rules, so there can't be a race condition between the thread and itself.

How is that ensuring "prevent race conditions"?

See above. You can only get a race if there is more than one thread updating. It is self-evident from the definition of a race condition.

[and] "other threads see the most up-to-date value"?

That is a consequence of what the Java memory model specifies about volatile. A read of a volatile variable is guaranteed to see the value of the most recent preceding write ... by any thread.

If thread A performs, say, counter++ when counter is 1, thread B could get in just before counter+1 is written back to memory and get a stale value 1 of counter.

In that scenario, there is no race condition. Thread B is seeing the most recently written value of counter. The counter has not been updated yet. That means that count++ is not atomic. But thread-safe and atomic do not mean the same thing. Atomic is a stronger guarantee. AND the JLS clearly states that count++ is not atomic for volatiles.

Now (obviously) if your application has multiple shared volatile variables being updated by different (single) threads, then the simple reasoning of the previous paragraphs can break down.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Just curious, if a single writer policy is used, would it be safe to use an atomic `lazySet` to update the state? Additionally, is there any difference between "perceived" atomicity in keeping with the single writer policy vs. thread-safety? –  Dec 19 '17 at 09:55