Understanding Java volatile: Memory Visibility, Lock Prefix, and Happens‑Before
This article explains the Java volatile keyword, covering its lightweight synchronization semantics, the underlying lock‑prefix implementation that forces cache line write‑backs, practical usage patterns such as state flags and double‑checked locking, and how volatile establishes a happens‑before relationship to guarantee memory visibility across threads.
The article gathers useful resources on Java concurrency and focuses on the volatile keyword, describing its role as the JVM's lightest synchronization mechanism and why many developers still prefer synchronized for thread‑safety.
Volatile ensures that writes to a variable are immediately visible to other threads, providing visibility without the heavy cost of full locks; a thread reading a volatile variable always sees the latest value.
Implementation-wise, the JVM emits a lock prefix instruction when a volatile write occurs. This forces the processor to write back the cache line to main memory and invalidates the same line in other CPUs' caches, thereby maintaining cache coherence.
Practical usage examples include:
public class ServerHandler {
private volatile isopen;
public void run() {
if (isopen) {
// isopen=true logic
} else {
// other logic
}
}
public void setIsopen(boolean isopen) {
this.isopen = isopen
}
}and the classic double‑checked locking singleton pattern, which should declare the instance field as volatile to avoid reordering issues:
class Singleton {
private volatile static Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}A more elegant lazy‑initialization approach uses the Initialization‑on‑Demand Holder idiom:
public class Singleton {
static class SingletonHolder {
static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return SingletonHolder.instance;
}
}Regarding memory visibility, ordinary variables are cached in a thread's working memory and updates are not automatically propagated, whereas volatile variables trigger the JVM to invalidate the working‑memory copy on reads and flush changes to main memory on writes, achieved through memory barriers.
The volatile‑happens‑before rule states that a write to a volatile field happens‑before any subsequent read of that same field. The following example demonstrates this ordering:
public class VolatileExample {
private int a = 0;
private volatile boolean flag = false;
public void writer(){
a = 1; //1
flag = true; //2
}
public void reader(){
if(flag){ //3
int i = a; //4
}
}
}In this code, the write to flag (statement 2) happens‑before the read in the reader method (statement 3), guaranteeing that the reader sees the updated value of a.
References include "Java Concurrency in Practice", "Effective Java High‑Concurrency Programming", and several online articles.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Big Data Technology & Architecture
Wang Zhiwu, a big data expert, dedicated to sharing big data technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
