Thread safety is critical in multithreaded Java programs. Without proper synchronization, shared data can lead to unpredictable behavior, bugs, and crashes. One of the most fundamental ways Java addresses this problem is through the synchronized
keyword.
In this guide, we’ll explore how synchronized
works, when to use it, and how it compares to other locking mechanisms like ReentrantLock
.
What Is Thread Safety?
Thread safety means ensuring that shared data is accessed and modified in a predictable and safe manner across multiple threads. Without thread safety:
- Data races can occur
- Inconsistent views of memory can arise
- Application behavior becomes unreliable
What Does synchronized
Do?
The synchronized
keyword:
- Prevents multiple threads from entering a critical section simultaneously
- Provides memory visibility (flushes variables from thread-local caches)
Syntax of synchronized
Synchronized Method
public synchronized void increment() {
counter++;
}
Synchronized Block
public void increment() {
synchronized (this) {
counter++;
}
}
Synchronized on Class Object
synchronized (MyClass.class) {
// static-level locking
}
Real-World Analogy
Imagine a bank teller counter. Only one customer can access the teller at a time — others must wait. The synchronized
keyword enforces the same rule for threads.
Thread Lifecycle Impact
synchronized
causes threads to enter the BLOCKED state if the monitor (lock) is already held.
Lifecycle path:
NEW → RUNNABLE → BLOCKED → RUNNING → TERMINATED
Java Memory Model & Visibility
synchronized
ensures happens-before relationship- Writes before unlocking are visible to reads after locking
- Prevents stale data and race conditions
Common Use Cases
- Counters and shared counters
- Updating shared collections
- Ensuring one-at-a-time access to sensitive code
Comparison with ReentrantLock
Feature | synchronized |
ReentrantLock |
---|---|---|
Simplicity | Easy to use | More verbose |
Locking granularity | Method/block only | Full control |
Interruptible | No | Yes |
Timeout support | No | Yes |
Fairness | No | Yes (optional) |
Best Practices
- Keep synchronized blocks short
- Lock on private final objects if possible
- Avoid nested locks (can cause deadlocks)
- Prefer block over method-level locking for flexibility
Anti-Patterns and Pitfalls
- Locking on
this
exposes lock to external code - Using string literals for locking
- Holding locks during I/O or long computation
- Over-synchronization can cause performance bottlenecks
Java Version Tracker
📌 What's New in Java Versions?
Java 8
- Lambdas for clean thread syntax
ConcurrentHashMap
improvements
Java 9
- Cleaner stack inspection APIs
Java 11+
var
support for more readable code
Java 21
- Virtual threads can use
synchronized
- Structured concurrency encourages scoped locking
- Scoped values for contextual thread data
Real-World Scenarios
- Inventory counters
- Banking transactions
- Thread-safe caching layers
- Atomic logging to shared file or output
Thread Coordination with synchronized
synchronized
works well with:
wait()
/notify()
/notifyAll()
- They must be used inside synchronized blocks
synchronized (lock) {
lock.wait(); // Releases lock temporarily
}
Design Patterns Using synchronized
- Monitor Object Pattern
- Producer-Consumer
- Thread-per-Message
- Guarded Suspension
Conclusion and Key Takeaways
synchronized
is the simplest way to protect critical sections- It ensures both mutual exclusion and memory visibility
- Proper usage prevents race conditions and maintains thread safety
- For advanced control, consider
ReentrantLock
orjava.util.concurrent
tools
FAQs
Q1: Does synchronized
block only one thread?
A: Yes, only one thread can hold the lock at a time.
Q2: Can static methods be synchronized?
A: Yes. It locks on the class object, not instance.
Q3: What happens if a thread holding a lock throws an exception?
A: The lock is automatically released.
Q4: Does synchronized
work with virtual threads?
A: Yes, fully supported as of Java 21.
Q5: Can I lock on null objects?
A: No. It throws NullPointerException
.
Q6: Can a thread enter multiple synchronized blocks?
A: Yes, if they are synchronized on different objects.
Q7: How is synchronized
different from volatile
?
A: synchronized
ensures atomicity + visibility, volatile
ensures visibility only.
Q8: Can I use synchronized
with arrays or collections?
A: Yes, but consider using concurrent collections for scalability.
Q9: What tool can help detect synchronization issues?
A: VisualVM, JFR, or profilers like YourKit.
Q10: Should I use synchronized
on a getter?
A: Only if the getter accesses shared mutable data.