Understanding Locks and Intrinsic Locks in Java Multithreading

Illustration for Understanding Locks and Intrinsic Locks in Java Multithreading
By Last updated:

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 as ReentrantLock, StampedLock, and ReadWriteLock.

🔄 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 with Runnable, 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

  1. What is the difference between intrinsic and explicit locks?
    Intrinsic locks are implicit via synchronized; explicit ones are controlled via classes like ReentrantLock.

  2. Can I use multiple locks in one class?
    Yes, but avoid deadlock by ordering acquisition consistently.

  3. What is a monitor?
    An internal entity that manages lock ownership per object.

  4. Is synchronized reentrant?
    Yes — the same thread can reacquire the same lock.

  5. When to prefer ReentrantLock?
    When you need timeout, fairness, or interruptible locks.

  6. What is a fair lock?
    Threads acquire locks in order of request — useful to avoid starvation.

  7. Is StampedLock better than ReadWriteLock?
    Yes, especially for optimistic reads and high performance.

  8. Can I use try-with-resources with locks?
    Only if the lock implements AutoCloseable, like StampedLock (with helper wrapper).

  9. Does volatile solve thread safety?
    Only for visibility, not atomicity or mutual exclusion.

  10. 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.