Optimistic vs Pessimistic Locks in Java: Concepts, Code & Comparison

This article explains the principles of optimistic and pessimistic locking in Java, shows how atomic classes and synchronized constructs implement them, provides runnable code examples, and compares their strategies, scenarios, performance, and typical implementations.

Xuanwu Backend Tech Stack
Xuanwu Backend Tech Stack
Xuanwu Backend Tech Stack
Optimistic vs Pessimistic Locks in Java: Concepts, Code & Comparison

Optimistic Lock

Optimistic lock assumes that conflicts are rare during concurrent access, so it does not lock when reading data and only checks for modifications when updating. If another thread changed the data, the update is aborted or retried. It is typically implemented using a version number or CAS mechanism.

In Java, atomic variable classes such as AtomicInteger and LongAdder use CAS to realize optimistic locking.

public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
    } while (!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    // CAS underlying implementation
    return var5;
}

Pessimistic Lock

Pessimistic lock assumes that conflicts are likely, so it locks the data when accessing it and releases the lock after the update. It is usually implemented with mutexes or read‑write locks.

In Java, Synchronized and ReentrantLock are typical pessimistic lock implementations. Because only one thread can hold the lock at a time, other threads block, leading to context switches and possible deadlocks.

public class PessimisticLockTest {
    private int counter = 0;

    // method locked to ensure only one thread modifies counter
    public synchronized void increment() {
        counter++;
    }

    public synchronized int getCounter() {
        return counter;
    }

    public static void main(String[] args) throws InterruptedException {
        PessimisticLockTest plt = new PessimisticLockTest();

        // create two threads to execute increment concurrently
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                plt.increment();
            }
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                plt.increment();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("Counter: " + plt.getCounter());
    }
}
// Counter: 2000

Comparison of Pessimistic and Optimistic Locks

Lock strategy: Pessimistic – lock before operating on the resource; Optimistic – no lock, assume no conflict, retry on conflict.

Applicable scenarios: Pessimistic – high concurrency with strict data consistency; Optimistic – high concurrency with low conflict probability.

Performance: Pessimistic – good under low concurrency, may cause thread blocking under high concurrency; Optimistic – better performance, especially when conflicts are rare.

Implementation: Pessimistic – Synchronized, ReentrantLock etc.; Optimistic – CAS, Atomic classes etc.

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.

SynchronizationOptimistic LockCASpessimistic-lockJava concurrency
Xuanwu Backend Tech Stack
Written by

Xuanwu Backend Tech Stack

Primarily covers fundamental Java concepts, mainstream frameworks, deep dives into underlying principles, and JVM internals.

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.