ARTICLE AD BOX
Java runs on more systems than CPUs with MESI. In fact, java predates MESI and is likely to outlive MESI too.
And yet, the JVM is also fast. This seems like an impossibility; how can java be defined in a way that ignores MESI while still enjoying the benefits?
The answer is the Java Memory Model. Java has a well defined spec about what you can and cannot expect, which is catered to make it possible for a JVM implementation to run fast (vs. e.g. a spec that says "every field write is instantly and guaranteed visible to every other thread" which would require a compliant JVM impl to stick so many guards and locks around every line that it'd run very slow. This is what python does, and why everybody complains about the 'GIL').
But that spec is not in terms of MESI, it's not in terms of any CPU architecture. It's a more abstract take on the concept that is designed to be able to be implemented on lots of hardware in a way that should mean java code can run fast.
The JMM is this model.
It operates on the notions of 'guarantees'. If something is not guaranteed that that simply means exactly what that word implies: At that point the JVM may do A, or B, and either way is fine. It may do A one day and B the next. And so on. If your code is dependent on A always happening, it is buggy, and in a way that is not testable (because a JVM may always do A; lack of guarantee does not guarantee that it never happens!).
The JMM is a large chapter, but the core of it is the Happens-Before concept. Certain lines have a HB relationship; the spec details precisely when an HB relationship is formed. If HB(A, B) means "A has a happens-before relationship relative to B", then the JMM says a JVM must guarantee that you cannot observe any state at point B such that it appears like it is the value that it had before A ran. Note that it's not actually a guarantee that 'B runs after A'; it is a guarantee that you won't be able to tell. Also, the JMM explicit disavows all timing: Nothing in the JMM claims that, for example, a field write always takes the same amount of nanoseconds or some such. In other words, if purely through timing out how long operations take, you can figure out things about a value, that does not count as 'observability'. This fortunately matches what most CPUs use as definiton of 'observable'.
If no HB is established, all bets are off and your code better not depend on any particular observability because if it does it is broken. The ways to establish HB are explicitly listed in the JMM. The highlights:
Natural HB: Within the confines of a single thread, HB(A, B) exists for all lines in execution order. In other words, int y = 0; y = 5; System.out.println(y); cannot possibly print anything but 5. This one is rather obvious. lock HB: The } of synchronized (a) {} is HB relative to the { in another synchronized (a) if the second block runs 'later'. And 'later' is a valid word to use for synchronized. Similar with volatile. join HB: the last line in a thread is HB relative to another thread's call to that thread's join method. start HB: the line thread.start() is HB relative to the first line in that thread.In your example, there's no HB. Hence, a JVM is free to have your code observe the state of the flag variable as it was either before or after. A JVM that reliably returns true is fine. A JVM that reliably returns false is fine. A JVM that flips a coin every time and returns the result of the coin is fine. A JVM that returns true 100% of the time during a million tests, but then returns false on the exact day you're demoing your app to that massive potential customer is.. fine.
NB: I did oversimplify a few things. For example, a JVM also guarantees that you cannot observe state that never was. In other words, if instead of a boolean, the field starts out as '5' and another thread sets it to 13 without establishing HB, then you can observe 5 or 13, either one. But if that thread starts saying its 13, it can't flip back to 5, and it can't report any other number. This guarantee is explicitly not made for long and double, but in practice no 64-bit JVM impl would give you sheared updates on those. In general this doesn't matter - there are only 2 very simple options. Either [A] you establish HB, or [B] your code is broken in a hard to test way.
