Java实例变量线程不可见问题:NoVisibility为何可能无限循环?
First, let's recap the classic NoVisibility example that's likely in your book—this makes it easier to walk through the problem clearly:
public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread { public void run() { // This loop might run forever! while (!ready) { Thread.yield(); } System.out.println(number); } } public static void main(String[] args) throws InterruptedException { new ReaderThread().start(); Thread.sleep(1000); number = 42; ready = true; } }
Your confusion is totally valid—why would ready = true seem like it never "sticks" for the reader thread? Let's break this down step by step:
Thread-local caching is the core culprit
Java's memory model lets each thread maintain its own local cache of shared variables, instead of reading/writing directly to main memory every time. This is a critical performance optimization—accessing local CPU cache is way faster than main memory.When the
ReaderThreadstarts, it loads the initial value ofready(false) into its local cache. As it runs the tightwhile (!ready)loop, the CPU might optimize this repeated read to only check its local cache, never going back to main memory to see if the value changed.No visibility guarantees without synchronization
When the main thread setsready = true, there's no rule (without explicit synchronization) forcing it to immediately flush that update to main memory. Even if it does eventually flush the value to main memory, there's no rule forcing theReaderThreadto discard its local cache and re-read the fresh value from main memory.It's not that the assignment "didn't happen"—the main thread definitely executes
ready = true. The problem is that this update never becomes visible to the reader thread, thanks to the lack of synchronization rules enforcing cache consistency.Why the infinite loop?
Exactly as you suspected: theReaderThreadis stuck reading its local cache's stalefalsevalue, while the main thread'sready = trueupdate either never makes it to main memory, or the reader thread never checks main memory again. The loop runs forever because the reader never sees the updated value.
How to fix it?
The simplest fix is to mark the ready variable as volatile:
private static volatile boolean ready;
The volatile keyword tells the JVM three key things:
- Every read of this variable must come directly from main memory, not the thread's local cache.
- Every write to this variable must be immediately flushed to main memory.
- No instruction reordering around reads/writes of this variable (which also ensures consistency, like making sure
number = 42is visible beforeready = true).
Alternatively, you could wrap the read and write operations in synchronized blocks, which also guarantees visibility and mutual exclusion.
内容的提问来源于stack exchange,提问作者zonyang

