Thread Synchronization in Java: A Deep Dive
Introduction
In modern applications, multi-threading is essential for improving performance and efficiency. However, when multiple threads access shared resources concurrently, it can lead to race conditions, inconsistent data, and unpredictable results. Thread synchronization ensures that only one thread accesses a critical section of code at a time, preventing such issues.
Why Do We Need Synchronization?
Consider the following example where two threads increment a shared counter:
class Counter {
int count;
public void setCount() {
count++; // Not thread-safe
}
}
public class Demo {
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.setCount();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.setCount();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Count: " + counter.count);
}
}
Actual Result : Count 1956(due to race conditions).
Expected result : Count 2000
Types of Thread Synchronization
1. Synchronized Methods
A method can be synchronized to allow only one thread at a time to execute it.
class Counter {
int count;
public synchronized void setCount() {
count++; // Thread-safe
}
}
✅ Ensures correctness but might reduce performance due to thread blocking.
2. Synchronized Blocks
Instead of synchronizing an entire method, we can synchronize only a specific block of code.
class Counter {
int count;
public void setCount() {
synchronized (this) {
count++;
}
}
}
✅ More efficient than synchronizing the entire method.
3. Using ReentrantLock (Explicit Locking)
Java’s ReentrantLock provides better control than synchronized and supports features like fairness and tryLock().
import java.util.concurrent.locks.ReentrantLock;
class Counter {
int count;
private final ReentrantLock lock = new ReentrantLock();
public void setCount() {
lock.lock(); // Acquire lock
try {
count++;
} finally {
lock.unlock(); // Release lock
}
}
}
✅ Offers more flexibility but requires manual locking/unlocking.
Conclusion
Thread synchronization is crucial for preventing race conditions in multi-threaded applications. While synchronized and ReentrantLock are useful for critical sections.
Reference :