7

I'm reading the book Crack Code Interview recently, but there's one paragraph confusing me a lot on page 257:

A thread is a particular execution path of a process; when one thread modifies a process resource, the change is immediately visible to sibling threads.

IIRC, if one thread make a change to a variable, the change will firstly save in the CPU cache (say, L1 cache), and will not guarantee to synchronize to other threads unless the variable is declared as volatile.

Am I right?

Michael Petrotta
  • 59,888
  • 27
  • 145
  • 179
WoooHaaaa
  • 19,732
  • 32
  • 90
  • 138
  • 4
    If you're talking about volatile, you need to state the language, too. In C and C++, volatile is generally useless for thread-synchronization, while in Java it is useful. – Ulrich Eckhardt Apr 28 '13 at 06:46
  • @doomster, thanks but i believe `volatile` play the same role in Java and C++, could you explain why it is different from your side ? – WoooHaaaa Apr 28 '13 at 06:50
  • 4
    They are similar in intent, but not equivalent. In Java, you have very specific guarantees concerning their behaviour in multithreaded programs. For C++, see http://stackoverflow.com/q/2484980/1968182. – Ulrich Eckhardt Apr 28 '13 at 08:16
  • @MrROY: In C and C++, `volatile` has no defined semantics with respect to threads. In Java, it does. There is no thread synchronization case in C or C++ where `volatile` is sufficient while in Java, there are. – David Schwartz Apr 28 '13 at 16:57

2 Answers2

10

Nope, you're wrong. But this is a very common misunderstanding.

Every modern multi-core CPU has hardware cache coherence. The L1, and similar caches, are invisible. CPU caches like the L1 cache have nothing to do with memory visibility.

Changes are visible immediately when a thread modifies a process resource. The issue is optimizations that cause process resources not to be modified in precisely the order the code specifies.

If your code has k = j; i = 4; if (j == 2) foo(); an optimizer might see that your first assignment reads the value of j. So it might not bother reading it again when you compare it to 2 since it "knows" that it can't have changed. However, another thread might have changed it. So optimizations of some kinds need to be disabled when synchronization between threads is required. That's what things like volatile do.

If compilers and CPUs made no optimizations and executed a program precisely as it was written, volatile would never be needed. Memory visibility is about optimizations in code (some done by the compiler, some by the CPU), not caches.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • "CPU caches .. have nothing to do with memory visibility" - Why? Many texts suggest otherwise. For example, from the JMM FAQ(http://www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html#synchronization): "After we exit a synchronized block, we release the monitor, which has the effect of flushing the cache to main memory, so that writes made by this thread can be visible to other threads" – Eyal Schneider Apr 28 '13 at 05:20
  • 2
    @EyalSchneider: The JMM is not talking about any actual implementation but about a notional system that *might* do anything at all. *If* you had caches that weren't hardware coherent, you'd have to flush them. Notice it says it "has the **effect** of flushing the cache to main memory", not that it actually flushes caches. (And, in practice, no caches are flushed at all. Only memory barriers are used which bypass certain CPU optimizations such as speculative fetches and posted writes.) – David Schwartz Apr 28 '13 at 05:29
  • Thanks David, as you said if one thread read `j` and then another thread change it, the first thread will not notice it. So the text I quoted is actually wrong ? – WoooHaaaa Apr 28 '13 at 05:46
  • And, could you explain how `volatile` works in the code example you gave ? – WoooHaaaa Apr 28 '13 at 05:49
  • @MrROY: If the second thread in fact changes it, and the first thread in fact reads it, then the second thread will notice it. The issue is that optimizations may mean that the second thread didn't actually change it or the first thread didn't actually read it. – David Schwartz Apr 28 '13 at 16:56
2

I think the text you are quoting is incorrect. The whole idea of the Java Memory Model is to deal with the complex optimizations by modern software & hardware, so that programmers can determine what writes are visible by the respective reads in other threads.

Unless a program in Java is properly synchronized, you can't guarantee that changes by one thread are immediately visible to other threads. Maybe the text refers to a very specific (and weak) memory model.

Usage of volatile variables is just one way to synchronize threads, and it's not suitable for all scenarios.

--Edit--

I think I understand the confusion now... I agree with David Schwartz, assuming that:

1) "modifies a process resource" means the actual change of the resource, not just the execution of a write instruction written in some high level computer language.

2) "is immediately visible to sibling threads" means that other threads are able to see it; it doesn't mean that a thread in your program will necessarily see it. You may still need to use synchronization tools in order to disable optimizations that bypass the actual access to the resource.

Eyal Schneider
  • 22,166
  • 5
  • 47
  • 78
  • You are both wrong and right. You are right that complex optimizations by modern software and hardware have to be dealt with. But he asked about CPU caches, not "complex optimizations". CPU caches are made invisible by cache coherence hardware. In fact, on modern Intel CPUs, the primary purpose of the L3 cache is to accelerate inter-core operations (and on almost all multi-core CPUs, a secondary purpose of the L2 cache is to do so). If you had to work around them or flush them to synchronize between threads, they would make one of the things they're intended to improve even worse. – David Schwartz Apr 28 '13 at 05:32
  • @DavidSchwartz: I wasn't talking about particular caches in my response. I just pointed out that the text quoted in the question is incorrect and misleading. – Eyal Schneider Apr 28 '13 at 05:39
  • 1
    @DavidSchwartz: Ok, I understand. It depends on how you interpret "modifies a process resource". There's a difference between "modifies a variable" and "modifies main memory". The latter isn't necessarily the immediate outcome of the former. – Eyal Schneider Apr 28 '13 at 19:00
  • Exactly. And I agree that probably most programmers reading that would either think it was incorrect or, worse, be mislead into thinking it was saying that memory visibility wasn't an issue between threads. (This is a very subtle issue that very few programmers understand, far fewer than the number that *think* they understand it.) – David Schwartz Apr 28 '13 at 19:53
  • 1
    @DavidSchwartz, just for general interest: the Cell processor in the PS3 is a rare example of 'caches' that aren't invisible. I'm talking about the SRAM on the 8 SPEs. As a programmer one has sole responsibility for loading / unloading that memory. It makes for complicated programming, but get it right and you can get tremendous performance. In that sense the Cell has simple hardware (ie no cache control hardware), complex software. Intel and AMD CPUs have complex cache hardware to allow for simple software. Guess which approach has sold better... Cell was a brave attempt by IBM, I liked it. – bazza Apr 29 '13 at 05:27