The visibility problem is a concurrency issue in the Java Memory Model where a write to a shared variable by one thread isn't guaranteed to be seen by another thread promptly, or at all, without proper synchronization. It stems from threads being allowed to cache variables locally, in registers or CPU caches, rather than reading and writing directly through main memory.
Key Points: • Without synchronization, the JMM permits a thread to keep reading a stale, locally-cached copy of a variable indefinitely, even after another thread has updated it. • This is distinct from atomicity or ordering problems; visibility is specifically about whether a write is ever observed by another thread at all. • The volatile keyword fixes visibility for a single variable by forcing all reads and writes through main memory and establishing a happens-before relationship. • synchronized blocks fix visibility more broadly: releasing a lock flushes changes to main memory, and acquiring that same lock guarantees the acquiring thread sees them. • A common symptom is a worker thread never noticing a boolean stop flag set by another thread, causing an infinite loop that only volatile or synchronization would fix.
Example: A background thread checking a plain boolean running flag in a tight while loop may never see it flip to false from another thread, because the JIT-optimized loop keeps reading a cached register value instead of checking main memory each iteration.
Code Example:
private volatile boolean running = true;
public void stop() {
running = false; // visible to other threads immediately
}Interview Tip: A concise interview answer is:
"The visibility problem happens when one thread's write to a shared variable isn't seen by another thread because it's reading a cached, stale value instead of going to main memory. Marking the variable volatile, or wrapping the access in synchronized, establishes a happens-before relationship that guarantees the update becomes visible."