-2

If we consider for a moment the following class and x86 architecture:

class Foo{
    int x;

    void increment() {
         compareAndSwap(x, x+1);
    }

    int getX() { return x; }
}

If we now assume one thread is calling increment, and the other periodically calls get, is there any possibility that the reader won't see an updated version of X? Shouldn't compareAndSwap actually lock the cache line across caches, update the value after comparison and then trigger a full barrier?

Bober02
  • 15,034
  • 31
  • 92
  • 178

2 Answers2

2

There is absolutely a possibility that the reader thread won't see an updated value of x. Ensuring that you get an up-to-date x is one of the main purposes of volatile.

Chapter 17 of the JLS talks about this. Specifically, check out:

JLS 17.4.1:

Two accesses to (reads of or writes to) the same variable are said to be conflicting if at least one of the accesses is a write.

And 17.4.5:

When a program contains two conflicting accesses (§17.4.1) that are not ordered by a happens-before relationship, it is said to contain a data race.

I won't list all of the details of how you establish a happens-before relationship (it's spelled out in 17.4.5), but suffice it to say, accessing a non-volatile field without synchronization does not do so.

That means your code has a data race, which means a lot of bets are off. For instance, the JVM is not required to come up with a single, sequential ordering for the actions; you can have one thread see one order, and another thread see another order.

To quote my last bit of JLS, this time from 17.4.3:

Sequential consistency is a very strong guarantee that is made about visibility and ordering in an execution of a program. Within a sequentially consistent execution, there is a total order over all individual actions (such as reads and writes) which is consistent with the order of the program, and each individual action is atomic and is immediately visible to every thread. (emphasis added)

That's what you want, and you're only guaranteed to have it if your code is free of data races. This pertains to your question because without sequentially consistent execution, the reader thread is essentially allowed to order things as "all other actions (including the reads), and then the writes" -- meaning that it never sees the reads -- even as other threads carry on without the writes being deferred to infinity.

This commonly comes up when people try to create a boolean stop variable to signal that a thread should stop, but neglect to make it volatile. Sometimes it works; many times, that thread will spin forever, since it never sees the write to stop.

See also this answer about using the volatile keyword.

yshavit
  • 42,327
  • 7
  • 87
  • 124
1

I think the answer is: it depends.

On the underlying jvm implementation - respectively that compareAndSwap that you introduced without further definitions.

In other words: I think it is reasonable to expect that a correct implementation for compareAndSwap should take cache lines into account.

That is the whole point of the jvm giving you guarantees, isn't it. If the jvm gives inconsistent results because of CPU caching - what kind of help would such guarantees offer us?!

Meaning: the whole point of the Java memory model is to provide certain "cast in stone" guarantees. They would be useless if CPU caching could break them.

But I also agree with the comment given - the fact that the variable isn't volatile means that different threads might in deed see "different" values of x.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • But `x` isn't volatile, so this is a data race (assuming multiple threads are calling `increment()`), and that means basically all bets are off in terms of guarantees. Pretty much the only guarantee you'll get is that `x` will be the default 0 or _some_ value that you wrote (ie, that you won't get word tearing). But which value that is... JVM can basically do whatever it wants. – yshavit Jan 05 '18 at 20:08
  • Ever heard of the Unsafe class? That class contains compare-and-swap methods. If you use that it might very well be possible to achieve that result without using volatile. Thus "it depends on the exact details". But you have a point though.. – GhostCat Jan 05 '18 at 20:12
  • Sure, a JVM can offer additional guarantees if it wants. But neither the JLS nor the JVM spec (to my knowledge) make any such guarantees, so to say that "Java" makes those guarantees would be a stretch, imo. – yshavit Jan 05 '18 at 20:14
  • My main problem is that perhaps x would be stored in register and never re-read from mem or cache in case of not adding volatile? – Bober02 Jan 05 '18 at 20:20
  • Unless you specify exactly what jvm is used, and how your "compareAndSwap" is implemented - nobody can give you an exact answer. When something is NOT volatile you have no guarantees about its "location" (and potential number of "copies"). – GhostCat Jan 05 '18 at 20:27