The volatile keyword in Java is used as a lightweight synchronization mechanism for variables shared between threads. It ensures visibility and ordering guarantees for multithreaded applications.
Key Features of volatile:
- Visibility Guarantee:
- When a thread modifies a
volatilevariable, the new value is immediately visible to other threads. - Threads always read the most recent value of the variable.
- When a thread modifies a
- Prevents Instruction Reordering:
- The
volatilekeyword prevents the compiler and processor from reordering instructions involving the variable. - This ensures consistency in concurrent environments.
- The
- No Atomicity:
- Operations on
volatilevariables are not atomic. - For compound actions (e.g.,
count++), use synchronization or classes likeAtomicInteger.
- Operations on
Syntax:
public class VolatileExample {
private volatile boolean flag = true;
public void stopRunning() {
flag = false; // Changes are visible to other threads immediately.
}
public void run() {
while (flag) {
// Thread will see the latest value of "flag".
}
}
}
Use Cases:
- Flags or Signals:
- Commonly used for signaling between threads (e.g., stopping a thread safely).
- Double-Checked Locking (Singleton Pattern):
- Ensures correct initialization of a singleton instance.
- Example:
public class Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
- Read-Write Scenarios:
- Use when one or more threads read a variable while another thread writes to it.
Example of Visibility Problem Without volatile:
public class VisibilityProblem {
private boolean running = true;
public void stopRunning() {
running = false; // May not be visible to other threads.
}
public void run() {
while (running) {
// Thread might read an outdated value of "running".
}
}
}
Fixed With volatile:
public class VisibilityProblemFixed {
private volatile boolean running = true;
public void stopRunning() {
running = false; // Changes are immediately visible to other threads.
}
public void run() {
while (running) {
// Thread will always read the updated value of "running".
}
}
}
Limitations of volatile:
- No Atomicity:
- Does not provide atomicity for operations like
count++orlist.add(item). - Use
synchronizedorjava.util.concurrentclasses (e.g.,AtomicInteger).
- Does not provide atomicity for operations like
- Overhead:
- Frequent writes to
volatilevariables may have performance costs due to memory synchronization.
- Frequent writes to
Key Points to Remember:
- Use
volatilefor variables shared between threads when only visibility and ordering guarantees are required. - Avoid using
volatilefor compound operations or complex synchronization scenarios. - Combine
volatilewith other synchronization mechanisms when atomicity is needed. - Alternatives for advanced concurrency:
synchronizedblocks/methods.java.util.concurrentclasses (e.g.,ReentrantLock,AtomicInteger).
Summary:
The volatile keyword is a simple yet powerful tool for ensuring visibility and ordering in multithreaded Java applications. While it does not replace proper synchronization mechanisms, it is ideal for use cases involving flags, signaling, and single-variable state sharing across threads.