Fundamentals 14 min read

Optimistic vs Pessimistic Locks and CAS Implementation in Java

The article explains the concepts of pessimistic and optimistic locking, details how CAS (Compare‑And‑Swap) implements optimistic locks in Java, discusses their advantages, drawbacks such as the ABA problem and high spin costs, and compares CAS usage with synchronized blocks in concurrent programming.

Java Captain
Java Captain
Java Captain
Optimistic vs Pessimistic Locks and CAS Implementation in Java

Pessimistic lock assumes the worst case: every data access acquires a lock because other threads might modify the data, leading to blocking, context switches, and possible priority inversion. Traditional relational databases and Java's synchronized keyword are typical examples.

Optimistic lock assumes conflicts are rare; it reads data without locking and validates at update time using mechanisms like version numbers. It improves throughput for read‑heavy workloads and is the basis of Java's atomic classes that rely on CAS.

CAS (Compare‑And‑Swap) is the core hardware primitive for optimistic locking. It atomically compares a memory location with an expected value and, if they match, swaps in a new value. The Java java.util.concurrent.atomic package implements CAS via native methods that ultimately invoke CPU instructions such as cmpxchg .

Example of an atomic class using CAS: public class AtomicInteger extends Number implements java.io.Serializable { private volatile int value; public final int get() { return value; } public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } }

CAS also has drawbacks: the ABA problem (a value may change from A to B and back to A, misleading the CAS), high CPU cost when spinning for long periods, and inability to atomically update multiple variables without additional techniques such as AtomicStampedReference or combining fields.

Java provides AtomicStampedReference to attach a stamp (version) to a reference, solving the ABA issue by requiring both the reference and its stamp to match before updating.

When choosing between CAS and synchronized , CAS is faster under low contention because it avoids kernel transitions, but under high contention the spin‑retry overhead can make synchronized (which now uses lock‑free queues and adaptive spinning) more efficient.

The java.util.concurrent package builds higher‑level constructs (locks, executors, queues) on top of volatile variables, CAS operations, and atomic classes, following a pattern of declaring shared state as volatile, updating it with CAS, and using the memory semantics of volatile reads/writes for thread communication.

In the JVM, object allocation also uses CAS: threads allocate memory from the heap either via pointer bumping or a free‑list, and CAS ensures the allocation pointer update is atomic. To reduce contention, the JVM employs Thread‑Local Allocation Buffers (TLABs), which allocate most objects without CAS, falling back to CAS only when a buffer is exhausted.

Synchronizationoptimistic lockCASpessimistic lockAtomicJava Concurrency
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

0 followers
Reader feedback

How this landed with the community

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