The volatile keyword in Java is used in the context of multithreading to indicate that a variable’s value will be modified by different threads. Declaring a variable as volatile ensures that changes to that variable are immediately visible to all threads, which helps to avoid memory consistency errors.
Here are the key points about the volatile keyword:
- Visibility Guarantee: When a variable is declared as volatile, it ensures that any write to that variable is immediately made visible to other threads. This means that when one thread changes the value of a volatile variable, all other threads instantly see that change.
- No Caching: Volatile variables are not cached in registers or other CPU caches, so every read of a volatile variable reads the variable’s most recent value from main memory.
- Atomicity: While volatile ensures visibility, it does not provide atomicity. This means that compound actions (such as incrementing a variable) are not atomic even if the variable is volatile. For such atomic operations, you should use classes from the util.concurrent.atomic package, like AtomicInteger.
- Use Cases: Volatile is typically used for flags that indicate a status change, such as a boolean flag to stop a thread, or simple counters and state indicators where only single read or write operations are performed.
public class SharedResource {
// Volatile variable shared across threads
private volatile boolean flag = false;
public void writerThread() {
flag = true; // change made visible to all threads immediately
}
public void readerThread() {
if (flag) {
// read the updated value
System.out.println("Flag was updated!");
}
}
}
Code language: PHP (php)
ProgramÂ
//VolatileDemo.java public class VolatileDemo { private volatile boolean flag = false; public void writerThread() { try { Thread.sleep(1000); // Simulate some work with sleep } catch (InterruptedException e) { Thread.currentThread().interrupt(); } flag = true; // Set the flag to true System.out.println("Writer thread set flag to true"); } public void readerThread() { while (!flag) { // Wait until the flag is true } System.out.println("Reader thread detected flag is true"); } public static void main(String[] args) { VolatileDemo demo = new VolatileDemo(); Thread writer = new Thread(demo::writerThread); Thread reader = new Thread(demo::readerThread); reader.start(); writer.start(); try { reader.join(); writer.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } /* C:\>javac VolatileDemo.java C:\>java VolatileDemo Reader thread detected flag is true Writer thread set flag to true */
Important Considerations:
- Synchronized Blocks: While volatile variables provide a lighter-weight synchronization mechanism, they are not a substitute for synchronized blocks or methods. Use synchronized when you need to ensure atomicity and visibility for compound actions.
- Complex Synchronization: For more complex scenarios where multiple variables need to be synchronized together, or where you need to perform compound actions atomically, volatile alone is not sufficient. In such cases, use higher-level concurrency constructs from the util.concurrent package.
- Double-checked Locking: One common use of volatile is in the double-checked locking pattern, often used in singleton implementations.
The volatile
keyword in Java is a lightweight tool used to ensure visibility of changes to variables across threads. When a variable is declared volatile
, any update made by one thread is immediately visible to all other threads. This helps avoid stale or cached values being used in concurrent environments.
However, volatile
does not guarantee atomicity, making it suitable only for simple flags or state variables (like stop signals or configuration switches). For compound actions (e.g., incrementing counters), atomic classes or explicit synchronization mechanisms like synchronized
or Lock
should be used instead.