In Java programming, the volatile keyword signifies that a variable's value could be altered by distinct threads concurrently. This keyword is also employed to ensure the thread safety of classes, allowing multiple threads to access methods and instances of these classes simultaneously without issues. The volatile keyword can be applied to both primitive data types and objects, but it is not compatible with classes or methods. Notably, it prevents the caching of variable values and mandates that variables are always read from the primary memory source.
However, it is used with variables. Essentially, it guarantees visibility and atomicity of reads and writes to the variable. It prevents the compiler from reordering of code.
The values stored in a specific device register are subject to change unpredictably. To prevent the compiler from optimizing these accesses, it's essential to use the volatile keyword.
Example
class Test
{
static int var=5;
}
Consider a scenario where two threads are concurrently operating on a shared class. Each thread executes on separate processors and possesses its own local instance of the variable "var." When one thread alters the value of its local "var," this modification does not propagate to the original variable stored in the main memory. This situation can result in data inconsistency as the second thread remains unaware of the updated value.
class Test
{
static volatile int var =5;
}
In the provided illustration, static variables represent class members that are accessible across all objects, with a single instance stored in the primary memory. Volatile variables ensure that their values are not cached, requiring all read and write operations to directly involve the main memory.
Example: volatile Keyword
Imagine a situation where numerous threads are interacting with a shared variable without adequate synchronization. If synchronization is lacking, modifications made by one thread may not be observable to others, resulting in unpredictable outcomes. The volatile keyword plays a crucial role in addressing this issue. We've established a class that increments the counter value. Within the VolatileThread.java file, the run function retrieves both the updated value and the old value at the thread's initiation. In the primary class, we create an array containing these threads.
File Name: VolatileData.java
public class VolatileData
{
private volatile int counter = 0;
public int getCounter()
{
return counter;
}
public void increaseCounter()
{
++counter; //increases the value of counter by 1
}
}
File Name: VolatileThread.java
VolatileThread.java
public class VolatileThread extends Thread
{
private final VolatileData data;
public VolatileThread(VolatileData data)
{
this.data = data;
}
@Override
public void run()
{
int oldValue = data.getCounter();
System.out.println("[Thread " + Thread.currentThread().getId() + "]: Old value = " + oldValue);
data.increaseCounter();
int newValue = data.getCounter();
System.out.println("[Thread " + Thread.currentThread().getId() + "]: New value = " + newValue);
}
}
File Name: Main.java
Example
public class Main {
private final static int noOfThreads = 2;
public static void main(String[] args) throws InterruptedException {
VolatileData volatileData = new VolatileData(); // object of VolatileData class
Thread[] threads = new Thread[noOfThreads]; // creating Thread array
for (int i = 0; i < noOfThreads; ++i)
threads[i] = new VolatileThread(volatileData);
for (int i = 0; i < noOfThreads; ++i)
threads[i].start(); // starts all reader threads
for (int i = 0; i < noOfThreads; ++i)
threads[i].join(); // wait for all threads
}
}
Output:
[Thread 22]: Old value = 0
[Thread 22]: New value = 1
[Thread 21]: Old value = 0
[Thread 21]: New value = 2
Within Java programming, the volatile keyword is crucial for managing concurrency as it guarantees the visibility and coherence of shared variables among various threads. Its application helps prevent intricate concurrency issues stemming from outdated or conflicting data. Nevertheless, it is crucial to apply volatile thoughtfully and grasp its constraints.
When to use it?
The volatile keyword is primarily useful in scenarios where variables are shared among multiple threads, and their values are frequently updated by one thread and read by others. Common use cases for volatile variables include flags to control thread execution, status indicators, and simple variables used in double-checked locking patterns.
- We can use a volatile variable if we want to read and write long and double variables
- It can be used as an alternative way of achieving synchronization in Java.
- All reader threads will see the updated value of the volatile variable after completing the write operation. If we are not using the volatile keyword, different reader threads may see other
- It is used to inform the compiler that multiple threads will access a particular statement. It prevents the compiler from doing any reordering or any optimization.
- If we do not use a volatile variable, the compiler can reorder the code, free to write in cache value of the volatile variable instead of reading from the main memory.
- We can use the volatile keyword with variables. Using the volatile keyword with classes and methods is illegal.
- It guarantees that the value of the volatile variable will always be read from the main memory, not from the local thread cache.
- If we declared a variable as volatile, reads and Writes are atomic
- It reduces the risk of memory consistency error.
- Any write to a volatile variable establishes a happen-before relationship with successive reads of that same variable.
- When a thread writes to a volatile variable, the write operation is immediately visible to other threads.
- The volatile variable that is an object reference may be null.
- When a variable is not shared between multiple threads, we do not need to use the volatile keyword with that variable.
Important Points to Remember
Difference Between synchronized and volatile Keyword
The volatile keyword cannot be used as a replacement for the synchronized keyword, but it can serve as an alternative in specific scenarios. The distinctions between them include:
| volatile Keyword | synchronized Keyword |
|---|---|
| The volatile keyword is a field modifier. | The synchronized keyword modifies code blocks and methods. |
| The thread cannot be blocked for waiting in case of volatile. | Threads can be blocked while waiting in case of synchronized. |
| It improves thread performance. | Synchronized methods degrade the thread performance. |
| It synchronizes the value of one variable at a time between thread memory and main memory. | It synchronizes the value of all variables between thread memory and main memory. |
| Volatile fields are not subject to compiler optimization. | Synchronize is subject to compiler optimization. |
| It does not provide mutual exclusion; multiple threads can access the variable simultaneously. | It provides mutual exclusion; only one thread can access the synchronized block or method at a time. |
| It is suitable for cases where threads perform only read/write operations on a single variable. | It is suitable for scenarios where operations involve multiple variables or compound actions (read-modify-write). |
| It cannot be used to ensure the atomicity of compound actions. | It ensures the atomicity of entire code blocks or methods. |
| Lightweight and less expensive in terms of system resources. | More resource-intensive due to the locking mechanism. |
| It cannot participate in inter-thread signalling using wait/notify. | It can use wait(), notify(), and notifyAll() for inter-thread signalling. |
Conclusion
The volatile keyword is utilized to ensure visibility between threads in order to reduce memory consistency issues. By using the volatile keyword, all threads can instantly see write operations, providing a simpler option compared to using locking mechanisms in certain situations where it is required.
The volatile keyword is commonly utilized for simple flags and indicator variables, as well as for read and write operations. It does not, however, provide support for atomic data operations. It is crucial for users to use volatile judiciously, understanding its limitations and suitable use cases. When applied correctly, volatile enhances the safety and efficiency of developing concurrent applications.
volatile Keyword MCQs
- Which of the following is true about the volatile keyword in Java?
- It guarantees atomicity for compound operations.
- It ensures visibility of changes to variables across threads.
- It can be used with methods and classes.
- It forces all variables to be synchronized.
Explanation: volatile ensures that the value of the variable is always read from the main memory, providing visibility, but not atomicity for compound operations like x++.
- What happens when a variable is declared as volatile in Java?
- Each thread caches the variable.
- The variable is only accessible by the declaring thread.
- Read and write operations are always performed on main memory.
- Threads are synchronized automatically.
Explanation: Declaring a variable volatile tells the JVM not to cache the variable in the thread-local memory, ensuring it always fetches from main memory.
- Which of the following happens when a thread writes to a volatile variable?
- It locks the variable to prevent access.
- It caches the variable in thread-local memory.
- It makes the write visible to all other threads immediately.
- It optimizes the access path for speed.
Explanation: Writing to a volatile variable establishes a happens-before relationship, making the change immediately visible to all threads.
- In which scenario should you use the volatile keyword?
- When a variable is not shared between threads.
- When only one thread reads and writes the variable.
- When multiple threads read and write a shared variable and you want to ensure visibility.
- When you want to block threads from accessing a variable.
Explanation: volatile is appropriate for shared variables that are written by one thread and read by others to ensure visibility of the latest value.
- What is the difference between synchronized and volatile in Java?
- Both provide atomicity and visibility.
- volatile blocks other threads; synchronized does not.
- synchronized provides both visibility and atomicity; volatile provides only visibility.
- volatile provides better control than synchronized.
In Java, the keyword "volatile" guarantees visibility of changes between threads, whereas using "synchronized" ensures both visibility and atomicity, which is particularly beneficial for handling critical sections of code.