The volatile keyword in Java serves as a lightweight synchronization mechanism. When a shared variable is declared as volatile, any thread reading that variable is guaranteed to see the most recent value written by another thread. This prevents stale data issues without incurring the higher overhead of context switching and thread blocking associated with heavier locks such as synchronized or Lock. However, because of its minimal overhead, volatile offers a narrower scope of protection. It ensures thread safety only in limited scenarios. Its two primary functions are ensuring memory visibility and preventing certain instruction reordering optimizations.
Visibility
In a multithreaded environment, each thread may maintain a local copy of variables in its working memory. With non-volatile variables, modifications made by one thread might not be immediately apparent to others, as their local copies are not automatically synchronized with the main memory. The volatile keyword ensures that any update to a variable is written directly to main memory and is instantly visible to all other threads.
Visibility Example
Consider the following structured code:
public class WorkerProcess extends Thread {
private volatile boolean active = true;
public void run() {
while (active) {
// thread A performs its task
}
}
public void terminate() {
active = false;
}
}
In this example, thread A continues looping as long as active is true. When another thread calls terminate(), it sets the flag to false. If active were not declared volatile, thread A might keep running indefinitely because it wouldn't immediately detect the chenge to the shared variable. With volatile, the write is instantly flushed to main memory, and a cache-coherence mechanism (typically involving a lock-prefix instruction on compatible hardware) invalidates stale copies in other caches, ensuring the reading thread sees the updated value.
Preventing Instruction Reordering
To improve execution efficiency, compilers and CPU pipelines may reorder instructions as long as the result appears consistent within a single thread. However, for variables marked as volatile, reads and writes establish memory barriers that constrain reordering both before and after the volatile access. This is critical for maintaining correctness in concurrent code.
A notable use case is the Double-Checked Locking pattern in the Singleton design:
public class AppConfig {
private static volatile AppConfig uniqueConfig;
private AppConfig() {}
public static AppConfig getConfig() {
if (uniqueConfig == null) {
synchronized (AppConfig.class) {
if (uniqueConfig == null) {
uniqueConfig = new AppConfig();
}
}
}
return uniqueConfig;
}
}
Using synchronized ensures that only one thread can enter the critical section, thereby instantiating the Singleton object exactly once. However, subtle reorderings can still cause issues. The assignment uniqueConfig = new AppConfig(); is not an atomic operation and can be broken down into three steps:
- Allocate memory for the object.
- Initialize the object within that memory.
- Assign the memory reference to the
uniqueConfigvariable.
Without volatile, the compiler or processor could reorder these steps, effectively performing step 1, then step 3, and finally step 2. In that scenario, another thread checking if (uniqueConfig == null) might observe a non-null reference pointing to an uninitialized object, leading to a NullPointerException when the object is subsequently used. Declaring the field volatile prohibits this harmful reordering, guaranteeing that the reference is published only after the constructor completes.
Important Considerations
-
Visibility, Not Atomicity:
volatileensures that a read sees the latest write, but it does not make compound actions atomic. Operations likecount++on avolatile int countare not thread-safe because they consist of multiple read-modify-write steps. -
Ideal for Simple State Flags:
volatileis best suited for status flags, such as termination signals or initialization-complete indicators. One thread sets the flag, and other threads immediately observe the change. -
Avoid Overuse: Because
volatilereads and writes can impose performance costs due to memory barriers, use it only when shared-mutable state truly requires low-latency visibility without the mutual-exclusion guarantees ofsynchronized,Lock, or atomic classes likeAtomicIntegerandAtomicLong.
volatile characters left: 243316:02volatile characters left: 243019:02volatile characters left: 242406:01volatile characters left: 242402:01volatile characters left: 242020:01volatile characters left: 241958:01volatile characters left: 241615:01volatile characters left: 241600:00volatile characters left: 241561:00volatile characters left: 241555:00volatile characters left: 241130:00volatile characters left: 240865:00volatile characters left: 240859:00volatile characters left: 240852:00volatile characters left: 240847:59volatile characters left: 240809:59volatile characters left: 240804:59volatile characters left: 240758:59volatile characters left: 240739:59volatile characters left: 240734:59volatile characters left: 240691:59volatile characters left: 240685:59volatile characters left: 240679:58volatile characters left: 240672:58volatile characters left: 240574:58volatile characters left: 240184:58volatile characters left: 239787:58volatile characters left: 239749:58volatile characters left: 239744:58volatile characters left: 239702:58volatile characters left: 239697:58volatile characters left: 239652:58volatile characters left: 239646:57volatile characters left: 239640:57volatile characters left: 239634:57volatile characters left: 239535:57volatile characters left: 239149:57volatile characters left: 239143:57volatile characters left: 238751:57volatile characters left: 238248:57volatile characters left: 237848:57volatile characters left: 237842:56volatile characters left: 237804:56volatile characters left: 237798:56volatile characters left: 237710:56volatile characters left: 237439:56volatile characters left: 236825:56volatile characters left: 236783:56volatile characters left: 236743:56volatile characters left: 236699:55volatile characters left: 236693:55volatile characters left: 236354:55volatile characters left: 235713:55volatile characters left: 235707:55volatile characters left: 235304:55volatile characters left: 235219:55volatile characters left: 235070:55volatile characters left: 235065:55volatile characters left: 235025:55volatile characters left: 234988:54volatile characters left: 234982:54volatile characters left: 234977:54volatile characters left: 234859:54volatile characters left: 234641:54volatile characters left: 234635:54volatile characters left: 234600:54volatile characters left: 234595:54volatile characters left: 234592:54volatile characters left: 234517:54volatile characters left: 234382:53volatile characters left: 234376:53volatile characters left: 234370:53volatile characters left: 234363:53volatile characters left: 234355:53volatile characters left: 234349:53volatile characters left: 234288:53volatile characters left: 234283:53volatile characters left: 234277:53volatile characters left: 234271:53volatile characters left: 234264:53volatile characters left: 234258:53volatile characters left: 234252:53volatile characters left: 234245:53volatile characters left: 234240:53volatile characters left: 234234:53volatile characters left: 234229:53volatile characters left: 234223:52volatile characters left: 234218:52volatile characters left: 234213:52volatile characters left: 234207:52volatile characters left: 234202:52volatile characters left: 234196:52volatile characters left: 234190:52volatile characters left: 234184:52volatile characters left: 234179:52volatile characters left: 234173:52volatile characters left: 234168:52volatile characters left: 234163:52volatile characters left: 234148:52volatile characters left: 234133:51volatile characters left: 234120:51volatile characters left: 234115:51volatile characters left: 234104:51volatile characters left: 234096:51volatile characters left: 234090:51volatile characters left: 234084:51volatile characters left: 234079:51volatile characters left: 234069:51volatile characters left: 234063:51volatile characters left: 234057:51volatile characters left: 234051:51volatile characters left: 234046:51volatile characters left: 234041:51volatile characters left: 234036:51volatile characters left: 234030:51volatile characters left: 234024:50volatile characters left: 233973:50volatile characters left: 233937:50volatile characters left: 233933:50volatile characters left: 233928:50volatile characters left: 233922:50volatile characters left: 233916:50volatile characters left: 233910:50volatile characters left: 233905:50volatile characters left: 233900:50volatile characters left: 233894:50volatile characters left: 233888:50volatile characters left: 233797:50volatile characters left: 233793:50volatile characters left: 233788:50volatile characters left: 233783:50volatile characters left: 233776:50volatile characters left: 233771:50volatile characters left: 233765:50volatile characters left: 233760:50volatile characters left: 233755:50volatile characters left: 233749:50volatile characters left: 233744:50volatile characters left: 233739:50volatile characters left: 233733:50volatile characters left: 233726:49volatile characters left: 233722:49volatile characters left: 233716:49volatile characters left: 233711:49volatile characters left: 233615:49volatile characters left: 233527:49volatile characters left: 233439:49volatile characters left: 233351:49volatile characters left: 233263:49volatile characters left: 233175:49volatile characters left: 233087:49volatile characters left: 232999:49volatile characters left: 232911:49volatile characters left: 232823:49volatile characters left: 232735:49: