Q:

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

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.

A:

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.

