Understanding Java Locks: Optimistic vs Pessimistic, Reentrant, Read‑Write, and More

This article explains the various Java lock mechanisms—including optimistic and pessimistic locks, exclusive and shared locks, reentrant, read‑write, fair/unfair, spin, segment locks, and lock‑escalation techniques—detailing their principles, Java implementations, usage scenarios, and optimization strategies.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Understanding Java Locks: Optimistic vs Pessimistic, Reentrant, Read‑Write, and More

Optimistic Lock and Pessimistic Lock

Pessimistic Lock

In code, a pessimistic lock assumes that other threads may modify the shared data, so each operation acquires the lock, causing other threads to block.

In Java, synchronized and ReentrantLock are typical pessimistic locks; container classes such as Hashtable also use them.

Optimistic Lock

An optimistic lock does not lock during normal operations; it checks for conflicts only when updating the data.

Java implements optimistic locking via version numbers and the CAS algorithm, e.g., the atomic classes in java.util.concurrent.atomic.

When to Use Each

Optimistic locks suit scenarios with few writes (low contention) because they avoid lock overhead, improving throughput. In write‑heavy, read‑light scenarios with high contention, pessimistic locks are more appropriate.

Exclusive Lock and Shared Lock

Exclusive Lock

An exclusive lock can be held by only one thread at a time; the holder can both read and modify the data.

In the JDK, synchronized and the Lock implementations in java.util.concurrent are exclusive locks.

Shared Lock

A shared lock can be held by multiple threads simultaneously, but only for reading; it cannot be upgraded to an exclusive lock.

The JDK class ReentrantReadWriteLock provides a shared (read) lock.

Mutex Lock and Read‑Write Lock

Mutex Lock

A mutex is a conventional exclusive lock that allows only one thread to access the protected resource at a time.

Read‑Write Lock

A read‑write lock separates read and write access: multiple threads may hold the read lock concurrently, while the write lock is exclusive and has higher priority.

JDK defines the ReadWriteLock interface:

public interface ReadWriteLock {
    /** Get the read lock */
    Lock readLock();

    /** Get the write lock */
    Lock writeLock();
}
ReentrantReadWriteLock

implements this interface.

Fair Lock and Unfair Lock

Fair Lock

A fair lock grants access to threads in the order they request it, similar to a queue.

/** Create a reentrant lock; true = fair lock, false = unfair lock (default) */
Lock lock = new ReentrantLock(true);

Unfair Lock

An unfair lock does not guarantee ordering; later threads may acquire the lock before earlier ones, which can cause priority inversion or starvation under high contention.

/** Create a reentrant lock; true = fair lock, false = unfair lock (default) */
Lock lock = new ReentrantLock(false);

Reentrant Lock

A reentrant (or recursive) lock allows the same thread to acquire the lock multiple times without deadlocking.

Both ReentrantLock and synchronized are reentrant.

public synchronized void methodA() throws Exception {
    // Do some work
    methodB();
}

public synchronized void methodB() throws Exception {
    // Do some work
}

Spin Lock

A spin lock makes a thread repeatedly loop (spin) while waiting for the lock, reducing the overhead of thread suspension.

In Java, AtomicInteger uses a spin‑CAS loop:

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!compareAndSwapInt(o, offset, v, v + delta));
    return v;
}

Segment Lock

A segment lock is a design that narrows lock granularity by locking only a portion of a data structure, such as a single bucket in a hash table.

Java's ConcurrentHashMap uses segment locks internally.

Lock Escalation (No‑Lock, Biased, Lightweight, Heavyweight)

Since JDK 1.6, the JVM can upgrade a lock through four states depending on contention: no‑lock (optimistic), biased lock, lightweight lock, and heavyweight lock.

No‑Lock corresponds to optimistic locking.

Biased Lock favors the first thread that acquires the lock when there is no contention.

Lightweight Lock uses spinning when contention is low.

Heavyweight Lock blocks other threads when contention becomes high; it is effectively a mutex.

Lock Optimization Techniques

Lock Coarsening

Combines multiple synchronized blocks into a larger one to reduce lock acquisition overhead.

private static final Object LOCK = new Object();
for (int i = 0; i < 100; i++) {
    synchronized (LOCK) {
        // do some work
    }
}
// After lock coarsening
synchronized (LOCK) {
    for (int i = 0; i < 100; i++) {
        // do some work
    }
}

Lock Elimination

The JVM can remove locks that are proven to be uncontended at runtime.

public String test(String s1, String s2) {
    StringBuffer stringBuffer = new StringBuffer();
    stringBuffer.append(s1);
    stringBuffer.append(s2);
    return stringBuffer.toString();
}

Although StringBuffer is synchronized, the method is thread‑safe without the lock, so the JVM eliminates it.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

concurrencyoptimistic lockpessimistic-lockread-write lockReentrant Lock
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.