Understanding Optimistic vs Pessimistic Locks and Advanced Java Lock Types
This article explains the concepts, differences, and appropriate use cases of optimistic and pessimistic locks, exclusive and shared locks, mutex and read‑write locks, fair and unfair locks, reentrant, spin, segment locks, lock upgrades, and lock‑optimization techniques in Java concurrency programming.
Optimistic and pessimistic locks
Exclusive and shared locks
Mutex and read‑write locks
Fair and non‑fair locks
Reentrant lock
Spin lock
Segment lock
Lock upgrade (no‑lock, biased, lightweight, heavyweight)
Lock optimization (coarsening, elimination)
Optimistic and Pessimistic Locks
Pessimistic Lock PessimisticLock behaves like a pessimistic person who always assumes the worst; before accessing shared data, a thread always acquires the lock, causing other threads to block.
In Java, synchronized and ReentrantLock are typical pessimistic locks, as are container classes such as Hashtable.
Optimistic Lock OptimisticLock behaves like an optimistic person who assumes no conflict; the thread reads without locking and checks for conflicts only when updating.
Optimistic locks are implemented with version numbers or CAS algorithms. In Java, the atomic classes in java.util.concurrent.atomic use CAS to provide optimistic locking.
When to Use Each
Optimistic locks suit scenarios with few writes (low contention) because they avoid lock overhead and increase throughput. In write‑heavy, read‑light scenarios with high contention, pessimistic locks are more appropriate.
Exclusive and Shared Locks
Exclusive Lock ExclusiveLock can be held by only one thread at a time; the holder can read and modify the data.
In the JDK, synchronized and the lock implementations in java.util.concurrent are exclusive locks.
Shared Lock SharedLock can be held by multiple threads simultaneously; holders can only read the data. ReentrantReadWriteLock provides a shared (read) lock and an exclusive (write) lock.
Mutex and Read‑Write Locks
Mutex Lock MutexLock is a conventional exclusive lock that allows only one thread to own it at a time.
Read‑Write Lock ReadWriteLock separates read and write operations: multiple threads may hold the read lock concurrently, while the write lock is exclusive.
JDK defines the ReadWriteLock interface:
public interface ReadWriteLock {
/** Get the read lock */
Lock readLock();
/** Get the write lock */
Lock writeLock();
} ReentrantReadWriteLockimplements this interface.
Fair and Non‑Fair Locks
Fair Lock
A fair lock grants access to threads in the order they requested it, similar to a queue.
In Java, a fair ReentrantLock can be created with new ReentrantLock(true).
Non‑Fair Lock
A non‑fair lock does not guarantee ordering; later threads may acquire the lock before earlier ones, which can lead to priority inversion or starvation.
Both synchronized and the default ReentrantLock are non‑fair.
Lock lock = new ReentrantLock(false);Reentrant Lock
ReentrantLock(also called a recursive lock) allows the same thread to acquire the lock multiple times without deadlocking.
Example:
public synchronized void methodA() throws Exception {
// do something
methodB();
}
public synchronized void methodB() throws Exception {
// do something
}When methodA calls methodB, the thread already holds the lock, so it does not need to acquire it again.
Spin Lock
SpinLockmakes a thread repeatedly loop (spin) while waiting for the lock instead of being suspended.
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;
}If the CAS fails, the loop retries.
Segment Lock
SegmentLockis a design that narrows lock granularity, allowing individual segments of a data structure to be locked independently.
Java's ConcurrentHashMap uses segment locks internally.
Lock Upgrade (No‑Lock, Biased, Lightweight, Heavyweight)
Since JDK 1.6, the JVM can upgrade a lock through four states: NoLock (optimistic), BiasedLock, LightweightLock, and HeavyweightLock, depending on contention.
No‑Lock is essentially an optimistic lock.
Biased Lock is biased toward the first thread that acquires it; if no other thread competes, the lock remains biased.
Lightweight Lock is used when contention appears; threads spin briefly before inflating to a heavyweight lock.
Heavyweight Lock blocks all other threads and corresponds to a traditional mutex.
The synchronized keyword internally performs this upgrade sequence: No‑Lock → Biased → Lightweight → Heavyweight.
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 coarsening
synchronized (LOCK) {
for (int i = 0; i < 100; i++) {
// do some work
}
}Lock Elimination
The JVM can detect that a lock protects data without contention and remove it entirely.
public String test(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1);
sb.append(s2);
return sb.toString();
}Because the method is thread‑safe, the JVM eliminates the synchronization inside StringBuffer.append.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
