For working professionals
For fresh graduates
More
A volatile keyword in Java is mainly used to change the value of a variable through various threads and is often used to make classes thread-safe. This means numerous strings can use this technique and instance of the classes simultaneously. The volatile keyword can be used either with primitive types or objects.
By marking a variable as “volatile”, we ensure its value is always read and written from the main memory rather than cached in a thread's local memory.
This tutorial guides you in understanding the concept of volatile keywords in Java, along with illustrative examples and critical differences between Synchronized, Transient, and Atomic keywords.
The volatile keyword in Java shows that the value of a variable can be altered through various strings simultaneously. When a variable is marked 'volatile', it guarantees that all reads and writes to that variable are performed directly from the primary memory, bypassing any caches that are thread-specific.
This ensures that any change made to the variable by one string will be promptly apparent to any remaining strings getting to it. Generally, the volatile keyword in Java provides a method for synchronizing the access and visibility of shared variables between numerous threads, making it a powerful tool for simultaneous programming and preventing inconsistencies in data or race conditions.
Here's an example that demonstrates the use of the volatile keyword in Java:
public class VolatileExample {
private volatile boolean flag = false;
public static void main(String[] args) {
VolatileExample example = new VolatileExample();
example.startThreads();
}
private void startThreads() {
Thread writerThread = new Thread(() -> {
try {
Thread.sleep(1000);
flag = true; // Updating the volatile variable
System.out.println("Flag is set to true.");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread readerThread = new Thread(() -> {
while (!flag) {
// Reading the volatile variable
}
System.out.println("Flag is now true.");
});
writerThread.start();
readerThread.start();
}
}
In this example, we have a VolatileExample class with a private volatile boolean variable called flag. The startThreads method creates a writer thread and a reader thread.
The writer thread sleeps for 1 second and then sets the flag variable to true. Since the flag is declared as volatile, this update is immediately visible to all other threads.
The reader thread continuously checks the value of the flag variable in a loop. It keeps looping until the flag becomes true. Once it detects that the flag is true, it prints a message indicating that it has been updated.
When you run this program, you'll see that the reader thread eventually exits the loop and prints, "Flag is now true." This demonstrates the visibility guarantee provided by the volatile keyword. Without the volatile keyword, the reader thread might run indefinitely, as it may not see the updated value of the flag variable made by the writer thread.
Using the volatile keyword ensures that the changes made to the flag variable by one thread are visible to other threads, avoiding potential visibility issues and ensuring thread-safe communication.
The synchronized keyword in Java allows synchronization and mutual exclusion in environments with multiple threads. It can be applied to techniques or blocks of code to guarantee that only a single thread can execute them. With synchronized keywords, one can prevent data races and keep up with the consistency of shared assets.
However, the volatile keyword in Java ensures that the variable's read and write operations maintain atomicity and visibility across numerous threads. Here is a table elucidating the stark differences between the two:
Parameters | Volatile | Synchronized |
Purpose | Ensures visibility and atomicity of variables | Provides mutual exclusion and atomicity of code |
Level of synchronization | Visibility | Mutual exclusion and atomicity |
Usage | Shared variables | Shared resources, critical sections, compound operations |
Provides atomicity | No | Yes |
Supports compound operations | No | Yes |
Mutual exclusion | No | Yes |
Performance impact | Lower overhead | Higher overhead |
Code complexity | Simpler | Requires explicit block or method synchronization |
Example of using the synchronized keyword for comparison:
public class SynchronizedExample {
private static int counter = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
synchronized (SynchronizedExample.class) {
for (int i = 0; i < 1000; i++) {
counter++;
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (SynchronizedExample.class) {
for (int i = 0; i < 1000; i++) {
counter--;
}
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final counter value: " + counter);
}
}
Here is an example that demonstrates the use of the transient keyword in Java:
After running the above program, this person.ser file gets generated as well:
Code:
import java.io.*;
class Person implements Serializable {
private String name;
private transient int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class TransientExample {
public static void main(String[] args) {
Person person = new Person("John", 25);
// Serialize the object to a file
try (FileOutputStream fileOut = new FileOutputStream("person.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(person);
System.out.println("Object serialized successfully.");
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize the object from the file
try (FileInputStream fileIn = new FileInputStream("person.ser");
ObjectInputStream in = new ObjectInputStream(fileIn)) {
Person deserializedPerson = (Person) in.readObject();
System.out.println("Object deserialized successfully.");
System.out.println("Name: " + deserializedPerson.getName());
System.out.println("Age: " + deserializedPerson.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
In this example, we have a Person class that implements the Serializable interface, indicating that objects of this class can be serialized. The Person class has two fields: name (a String) and age (an int). However, the age field is marked as transient.
We first create a Person object named "John" and the age “25”. We then serialize the object by writing it to a file named "person.ser". When we serialize the object, the age field, being transient, is excluded from the serialization process.
Later, we deserialize the object from the file. The deserialized object is assigned to the deserializedPerson variable. When we print the name and age of the deserialized person, we'll see that the name field is successfully deserialized and displayed, while the age field will have its default value of 0 (since it was not serialized).
Using the transient keyword, we control which fields of an object are included or excluded from the serialization process. This is useful when certain fields are not required to be persisted or transferred along with the object's state.
The transient keyword in Java provides synchronization and mutual exclusion in multi-threaded environments. It ensures that only one thread can access a synchronized block of code or a synchronized method at a time, thereby preventing data races and maintaining the consistency of shared resources.
Volatile keywords, on the other hand, do not directly relate to serialization and have no impact on the serialization process. Here is a table elucidating the stark differences between the two:
Parameters | Transient | Volatile |
Purpose | Serialization control | Thread visibility and atomicity |
Serialization | Excludes the variable from serialization | No direct impact on the serialization process |
Thread Visibility | No guarantee of visibility across threads | Guarantees visibility of changes across threads |
Atomicity | No guarantee of atomicity | Provides atomicity for simple read/write operations |
Usage | Serialization | Multithreaded programming |
In Java, the Atomic classes provide atomic operations, ensuring that operations on variables are performed atomically, without interference from other threads.
The volatile keyword does not guarantee atomicity by itself. It only ensures the visibility and order of variable access across threads. Here is a table elucidating the stark differences between the two:
Feature | Atomic Classes | Volatile Keyword |
Purpose | Atomic operations and thread safety | Visibility and ordering of variable access |
Atomicity | Provides atomic operations on variables | Does not guarantee atomicity by itself |
Thread Safety | Ensures thread safety for operations | Ensures visibility and ordering of variable access |
Use Cases | Performing atomic operations (e.g., incrementing) | Ensuring visibility of changes across multiple threads |
Compound Operations | Supports compound operations with atomic guarantees | Does not provide inherent thread safety for compound operations |
Synchronization | Implicitly provides synchronization through atomic operations | Does not provide mutual exclusion or synchronization |
Performance Impact | Relatively higher overhead due to atomic guarantees | Lower overhead compared to atomic operations |
Granularity of Control | Fine-grained control over individual variables | Applied at the variable level |
Usage Complexity | More complex due to specific atomic operations | Simpler, as it is applied at the variable declaration level |
Example of using the atomic keyword for comparison:
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
private static AtomicInteger counter = new AtomicInteger(0);
public static void main(String[] args) {
// Increment the counter atomically
counter.incrementAndGet();
// Retrieve the current value of the counter atomically
int currentValue = counter.get();
System.out.println("Current value of the counter: " + currentValue);
}
}
In conclusion, the volatile keyword in Java is a valuable tool for ensuring the visibility and atomicity of variables in concurrent programming. It allows multiple threads to safely access shared variables, preventing data inconsistencies and race conditions. Understanding the nuances of “volatile” is essential for developing robust and thread-safe applications.
To master these concepts in Java, you can take up some of the comprehensive and industry-relevant courses offered by upGrad to enhance your skills and knowledge in various domains.
1. What is the purpose of volatile keywords in Java?
The volatile keyword ensures the visibility and atomicity of variables in Java's simultaneous programming.
2. What is a volatile and transient keyword in Java?
The volatile keyword ensures visibility and atomicity of variables, while the transient keyword excludes variables from serialization.
3. Where are volatile variables stored in Java?
Volatile variables are stored in the main memory in Java.
Take the Free Java Quiz & See Your Rank!
Pavan Vadapalli
Director of Engineering @ upGrad. Motivated to leverage technology to solve problems. Seasoned leader for startups and fast moving orgs. Working …Read More
Talk to our experts. We are available 7 days a week, 9 AM to 12 AM (midnight)
Indian Nationals
1800 210 2020
Foreign Nationals
+918045604032
1.The above statistics depend on various factors and individual results may vary. Past performance is no guarantee of future results.
2.The student assumes full responsibility for all expenses associated with visas, travel, & related costs. upGrad does not provide any a.