Concurrency brings performance — but only when thread safety is ensured. Java offers multiple locking mechanisms, and at the heart of it all lies the intrinsic lock, better known as the monitor lock.
This tutorial explores both intrinsic and explicit locks in Java — when to use them, how they work internally, and the best practices around them.
🔐 What is a Lock in Java?
A lock is a synchronization mechanism that ensures only one thread can access a particular block of code or object at a time.
Java provides two main types of locks:
- Intrinsic locks (aka monitor locks) used with the
synchronized
keyword. - Explicit locks using classes from
java.util.concurrent.locks
such asReentrantLock
,StampedLock
, andReadWriteLock
.
🔄 Thread Lifecycle Refresher
Threads can be in the following states:
- NEW → RUNNABLE → RUNNING → BLOCKED/WAITING → TERMINATED
Locks primarily affect BLOCKED and WAITING states when a thread cannot proceed due to a lock held by another.
⚙️ Intrinsic Locks and synchronized
Method-level locking
public synchronized void increment() {
count++;
}
Block-level locking
synchronized (this) {
count++;
}
When a thread enters a synchronized block, it obtains the monitor lock. Other threads attempting the same must wait.
🔁 Explicit Locks
ReentrantLock
Lock lock = new ReentrantLock();
lock.lock();
try {
// critical section
} finally {
lock.unlock();
}
Benefits:
- Fine-grained control
- Try-locking with timeout
- Interruptible locking
StampedLock
Useful for optimistic and pessimistic read-write locking.
ReadWriteLock
Separates read and write access for better performance in read-heavy systems.
🚫 Pitfalls of Locking
- Deadlocks: when two threads wait on each other indefinitely.
- Starvation: a thread never gets access to a lock.
- Thread contention: multiple threads fight for the same lock, degrading performance.
🚀 Real-world Use Cases
- Protecting shared counters or caches
- Synchronizing access to files or logs
- Implementing producer–consumer pipelines
🧠 Alternatives and Enhancements
Feature | Alternative |
---|---|
synchronized | ReentrantLock, ReadWriteLock |
wait/notify | BlockingQueue, Condition |
Manual pooling | ExecutorService, ForkJoinPool |
Manual threads | Virtual threads (Java 21) |
📌 What's New in Java Concurrency (8–21)
- Java 8:
CompletableFuture
, lambdas withRunnable
, parallel streams - Java 9: Flow API for reactive streams
- Java 11: Enhancements in common pool and minor refinements
- Java 21: Structured concurrency, virtual threads (Project Loom), scoped values
🛠️ Best Practices
- Keep synchronized blocks small and focused
- Prefer explicit locks when you need control, fairness, or timeouts
- Avoid nested locks and circular dependencies
- Always release acquired locks (finally block or try-with-resources)
❓ FAQ
-
What is the difference between intrinsic and explicit locks?
Intrinsic locks are implicit viasynchronized
; explicit ones are controlled via classes likeReentrantLock
. -
Can I use multiple locks in one class?
Yes, but avoid deadlock by ordering acquisition consistently. -
What is a monitor?
An internal entity that manages lock ownership per object. -
Is
synchronized
reentrant?
Yes — the same thread can reacquire the same lock. -
When to prefer
ReentrantLock
?
When you need timeout, fairness, or interruptible locks. -
What is a fair lock?
Threads acquire locks in order of request — useful to avoid starvation. -
Is
StampedLock
better thanReadWriteLock
?
Yes, especially for optimistic reads and high performance. -
Can I use
try-with-resources
with locks?
Only if the lock implementsAutoCloseable
, likeStampedLock
(with helper wrapper). -
Does
volatile
solve thread safety?
Only for visibility, not atomicity or mutual exclusion. -
What are lightweight and biased locks?
JVM optimizations for synchronized — mostly transparent to developers.
🧾 Conclusion and Key Takeaways
- Intrinsic locks via
synchronized
are simple and effective for basic use. - Explicit locks offer fine control, flexibility, and advanced coordination.
- Use best practices to avoid contention, deadlocks, and complexity.
- Embrace modern concurrency tools and APIs introduced in Java 8–21.
Mastering locks is essential for building safe, fast, and scalable Java applications.