When to use volatile with multi threading?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
When working with multithreading in Java, understanding the use of the volatile keyword is crucial to ensure that threads can access and update shared variables safely. This article explores when to use volatile, how it works, and common use cases, along with potential limitations.
Understanding volatile
In Java, volatile is a keyword used to indicate that a variable's value will be modified by different threads. Declaring a variable as volatile ensures visibility and establishes that:
- Changes to a
volatilevariable by one thread are immediately visible to other threads. - There is no caching of the variable, meaning that every read of the variable will fetch the latest value from the main memory.
- It prevents instruction reordering concerning that variable.
Memory Visibility
One of the primary reasons to use volatile is to guarantee memory visibility. In Java's memory model, each thread has its own stack, where it can cache variables, leading to inconsistency when variables are updated in different threads. The volatile keyword prevents this, ensuring that all reads and writes go to the main memory directly.
Example of volatile
Consider a classic example of a flag that a thread uses to communicate with another thread.
In this example, the flag variable is declared as volatile. When one thread updates the flag in the writer() method, the change is immediately visible to the thread executing the reader() method.
When to Use volatile
1. Simple Flags or States
Use volatile for simple flags, state indicators, or conditionals where a single variable is shared among multiple threads, and updates are predictable, with atomic operations sufficing.
2. No Compound Actions
volatile should be used when the variable's operations are atomic (like read or write) but not when dealing with compound actions. For instance, if variables need to be incremented or require compound actions, then Atomic classes or synchronized blocks are more appropriate.
3. Lazy Initialization
volatile ensures the latest value is seen for variables that might be lazily initialized, where the initialized value only needs to be written once and thereafter can be read. An example is the Double-Checked Locking pattern:
This pattern checks the singleton status twice, once without locking and once within a lock, ensuring thread-safe lazy initialization.
Limitations of volatile
- Not for Compound Actions:
volatilecannot be used to ensure atomicity of compound operations, such as increments or checks followed by actions (e.g.,if(flag) doSomething()). Consider usingsynchronizedblocks orjava.util.concurrentatomic classes. - Order of Operations:
volatiledoes not ensure the order in which threads execute, it only guarantees visibility. For strict sequencing, higher synchronization is required. - Heavy Write Load: With numerous write operations,
volatilecan become a performance bottleneck because each operation bypasses caching.
Key Points Summary
| Usage | Suitable Contexts | Limitations |
| Simple flags and state indicators | Simple flags, read-mostly scenarios | Not for atomic compound actions |
| Memory Visibility | Visibility between threads | No guarantee of order or synchronization |
| Lazy Initialization | Singleton, lazy fields needing only a write-once guarantee | Performance issues under heavy write loads |
In conclusion, volatile is a powerful tool in Java threading for certain use cases, including simple flags and ensuring visibility between threads. However, understanding its limitations is key to applying it correctly without inadvertently introducing bugs or performance issues. When in doubt, consider complementing volatile with more robust constructs like synchronized blocks or higher-level concurrency abstractions.
Related reading
- When to using async when dealing with TcpClients?
- When using run_in_executor in asyncio, is the event loop executed in the main thread?
- When will ConcurrentDictionary TryRemove return false
- When would you call java's thread.run instead of thread.start?
- When using Spring Security, what is the proper way to obtain current username (i.e. SecurityContext) information in a bean?
- When would you use a WeakHashMap or a WeakReference?
- When you await on async call--is it really asynchronous programming in C
- WhenAll on the large number of Task

OOD Fundamentals
Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.