Volatile and Synchronized thread Synchronized Technique
Last Updated :
18 Jul, 2025
In multithreaded Java programs, multiple threads may access shared resources simultaneously, leading to unpredictable behavior or inconsistent data. To prevent this, Java provides mainly the following two mechanisms to ensure safe access and update of shared data:
- Synchronized keyword/block
- Volatile keyword
Synchronized Keyword
The synchronized keyword in Java is used to control access to shared resources in multithreaded environments. By applying it to a method or block of code, you ensure that only one thread at a time can execute that portion of code for a given object, thus maintaining data consistency and preventing race conditions.
Working of Synchronized Modifier:
- It can be applied to methods or blocks of code.
- It allows only one thread can be executed at a time.
- Every object has an intrinsic lock (or monitor lock). A thread must acquire this lock before it can execute a synchronized method or block associated with that object, ensuring mutual exclusion and thread safety.
- Excessive synchronization can cause thread contention, reducing performance.
Example: Create TaskRunner class.
- This program demonstrates the use of a synchronized method to prevent race conditions in a multithreaded environment.
- Each thread has its own counter, and the output shows only threadOne's task count.
TaskRunner.java:
Java
// Java program to demonstrate
// use of synchronized method in threads
import java.util.concurrent.TimeUnit;
class TaskRunner extends Thread {
private int taskCount;
// Synchronized run method to avoid race conditions
@Override public synchronized void run()
{
try {
TimeUnit.MILLISECONDS.sleep(
100); // Simulate delay
}
catch (InterruptedException e) {
e.printStackTrace();
}
taskCount++; // Safely increment the task count
}
public int getTaskCount() { return taskCount; }
}
public class Geeks {
public static void main(String[] args)
throws InterruptedException
{
TaskRunner threadOne = new TaskRunner();
TaskRunner threadTwo = new TaskRunner();
threadOne.start();
threadTwo.start();
threadOne.join();
threadTwo.join();
// Output will be 1 since we are only printing
// threadOne's task count
System.out.println("Final Task Count (Thread One): "
+ threadOne.getTaskCount());
}
}
OutputFinal Task Count (Thread One): 1
Explanation:
- You have a class TaskRunner that counts tasks using a variable taskCount.
- run() method is marked synchronized to keep it safe if multiple threads access shared data (though in this case, they don’t).
- Inside run(), the thread waits 100 milliseconds and then adds 1 to taskCount.
- In main(), you create two separate threads (threadOne and threadTwo).
- Each thread has its own copy of taskCount, starting at 0.
- Both threads run, but you only print the result from threadOne.
Volatile Keyword
The volatile keyword in Java ensures that all threads have a consistent view of a variable's value. It prevents caching of the variable's value by threads, ensuring that updates to the variable are immediately visible to other threads.
Working of Volatile Modifier:
- It applies only to variables.
- volatile guarantees visibility i.e. any write to a volatile variable is immediately visible to other threads.
- It does not guarantee atomicity, meaning operations like count++ (read-modify-write operations) can still result in inconsistent values
Example: Create ScoreTracker class.
- This program demonstrates the use of the volatile keyword in a multithreaded context to ensure visibility of shared variables.
- However, since score++ is not atomic, race conditions may occur, leading to a final result less than 2000.
ScoreTracker.java
Java
// Java program to demonstrate the
// volatile keyword with non-atomic operations
class ScoreTracker {
private volatile int score; // Volatile variable
public void updateScore()
{
score++; // Not an atomic operation
}
public int getScore() { return score; }
}
public class DemoApp {
public static void main(String[] args)
{
ScoreTracker tracker = new ScoreTracker();
Thread threadA = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
tracker.updateScore();
}
});
Thread threadB = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
tracker.updateScore();
}
});
threadA.start();
threadB.start();
try {
threadA.join();
threadB.join();
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Final Score: "
+ tracker.getScore());
}
}
Explanation:
- The class ScoreTracker has a volatile variable score that keeps track of the total score.
- Two threads (threadA and threadB) are created to update the score 1000 times each using score++.
- volatile ensures each thread sees the latest value of score, but score++ is not safe when used by multiple threads.
- That’s because score++ is not a single-step (atomic) operation.
- So, even though both threads run 1000 times, the final score is usually less than 2000.
- The program prints the final score after both threads finish.
Volatile vs Synchronized
Features | Synchronized | Volatile |
---|
Applies to | It applies only to blocks or methods. | It applies to variables only. |
---|
Purpose | It ensures mutual exclusion and visibility | It ensures visibility of changes to variables across threads |
---|
Performance | Performance is relatively low compared to volatile because of the acquisition and release of the lock. | Performance is relatively high compared to synchronized Keyword. |
---|
Deadlock Risk | Deadlock can arise if not used carefully with nested locks | No deadlock rise. |
---|