# Optimistic Locking vs Pessimistic Locking in Java: Key Differences and Implementation

> Understand optimistic locking vs pessimistic locking in Java. Learn how synchronized locks prevent conflicts while CAS operations optimize for rare contention.

- Repository: [Guide/JavaGuide](https://github.com/Snailclimb/JavaGuide)
- Tags: deep-dive
- Published: 2026-02-24

---

**Pessimistic locking prevents conflicts by acquiring exclusive locks before accessing shared data using `synchronized` or `ReentrantLock`, while optimistic locking assumes contention is rare and uses non-blocking compare-and-swap (CAS) operations or version checks that retry upon collision.**

Understanding the **difference between optimistic locking and pessimistic locking in Java** is essential for building high-performance concurrent applications. The Snailclimb/JavaGuide repository provides comprehensive documentation and runnable examples in [`docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md`](https://github.com/Snailclimb/JavaGuide/blob/main/docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md) that demonstrate how these two strategies protect shared mutable state. While both approaches prevent race conditions, they differ fundamentally in their assumptions about thread contention and their impact on system throughput.

## Fundamental Assumptions and Mechanisms

### Pessimistic Locking: Conflict is Inevitable

**Pessimistic locking** operates on the assumption that the resource *will* be contested. Threads acquire an exclusive lock before reading or writing, forcing other threads to block until the lock is released. This approach uses traditional synchronization mechanisms to serialize access to critical sections.

### Optimistic Locking: Conflict is Rare

**Optimistic locking** assumes the resource *won't* be contested during most operations. Threads perform reads and writes without acquiring locks, then verify at commit time that the data has not changed. If a conflict is detected, the operation retries until successful.

## Implementing Pessimistic Locking in Java

According to the JavaGuide source code, pessimistic strategies rely on the `java.util.concurrent.locks` package and language-level synchronization to block concurrent access.

### Using `synchronized` Blocks

The simplest form of pessimistic locking uses the `synchronized` keyword to create a monitor lock that serializes access to critical blocks.

```java
// 1. Pessimistic lock with synchronized
public void performSynchronisedTask() {
    synchronized (this) {
        // critical section – only one thread at a time
    }
}

```

This approach is implemented in [`docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md`](https://github.com/Snailclimb/JavaGuide/blob/main/docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md) and provides a straightforward mental model where threads block until the monitor becomes available.

### Using `ReentrantLock` for Explicit Control

For more flexible lock acquisition, `ReentrantLock` allows explicit locking and unlocking with timeout capabilities and interrupt handling.

```java
// 2. Pessimistic lock with ReentrantLock
private final Lock lock = new ReentrantLock();

public void performReentrantLockTask() {
    lock.lock();
    try {
        // critical section
    } finally {
        lock.unlock();   // always release in finally
    }
}

```

As documented in the repository, this pattern requires careful handling in a `finally` block to prevent deadlocks during exceptions.

## Implementing Optimistic Locking in Java

Optimistic strategies avoid blocking by using atomic operations and version validation, as detailed in the JavaGuide concurrent programming section.

### CAS-Based Updates with Atomic Classes

The `java.util.concurrent.atomic` package provides lock-free classes like `LongAdder` and `AtomicInteger` that use Compare-And-Swap (CAS) algorithms. These classes increment counters without blocking threads, making them ideal for high-throughput scenarios.

```java
// 3. Optimistic lock via CAS – LongAdder (high‑throughput counter)
LongAdder counter = new LongAdder();   // no explicit lock
counter.increment();                  // CAS‑based update

```

For manual control over the retry logic, implement a loop using `AtomicInteger.compareAndSet()`:

```java
// 5. Manual CAS loop on an AtomicInteger
AtomicInteger atomic = new AtomicInteger(0);
int expected, update;
do {
    expected = atomic.get();
    update = expected + 1;               // compute new value
} while (!atomic.compareAndSet(expected, update));

```

### Version-Field Strategy for Complex State

For database-style optimistic locking, maintain a version field that increments with each update. The operation only commits if the version remains unchanged since reading.

```java
// 4. Optimistic lock with a version field (DB‑style)
int currentVersion = row.getVersion();
int newBalance = row.getBalance() - 50;

// Try to update only if version unchanged
UPDATE accounts
SET balance = ?, version = version + 1
WHERE id = ? AND version = currentVersion;

```

This pattern appears in [`docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md`](https://github.com/Snailclimb/JavaGuide/blob/main/docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md) as a solution for banking-balance scenarios where transactions must validate consistency at commit time.

## Performance Characteristics and Selection Criteria

Choosing between these strategies depends on your contention profile and consistency requirements.

| Aspect | Pessimistic Locking | Optimistic Locking |
|--------|----------------------|--------------------|
| **Thread Blocking** | Yes—threads wait for lock release | No—threads retry on conflict |
| **Deadlock Risk** | Possible with incorrect lock ordering | Eliminated (no locks held) |
| **Best For** | High write ratios, multi-resource atomicity | Read-heavy workloads, single-variable updates |
| **CPU Impact** | Context-switch overhead during blocking | Busy-waiting during high contention |

**Pessimistic locking** excels when write contention is high or operations must span multiple resources atomically. **Optimistic locking** delivers superior performance for read-heavy scenarios with infrequent writes, such as counters and configuration flags.

## Summary

- **Pessimistic locking** acquires exclusive locks before access, using `synchronized` or `ReentrantLock` to prevent conflicts through thread blocking.
- **Optimistic locking** assumes low contention and uses non-blocking CAS operations or version checks, retrying when collisions occur.
- Use pessimistic strategies for high-write workloads requiring strong consistency across multiple resources.
- Use optimistic strategies for read-heavy applications where occasional retries cost less than systematic blocking.
- The Snailclimb/JavaGuide repository provides working implementations in [`docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md`](https://github.com/Snailclimb/JavaGuide/blob/main/docs/java/concurrent/optimistic-lock-and-pessimistic-lock.md) demonstrating both approaches.

## Frequently Asked Questions

### What is the main difference between optimistic and pessimistic locking?

Pessimistic locking assumes conflicts will occur and blocks threads with exclusive locks before operations begin, while optimistic locking assumes conflicts are rare and proceeds without locks, verifying data integrity at commit time and retrying if necessary.

### When should I use pessimistic locking over optimistic locking in Java?

Use pessimistic locking when your application has high write contention, requires atomic operations across multiple shared resources, or when the cost of retrying failed optimistic updates exceeds the cost of thread blocking and context switching.

### Can optimistic locking cause deadlocks?

No, optimistic locking cannot cause deadlocks because it does not acquire or hold locks during the read-modify-write cycle. However, under extreme write contention, excessive retry loops can cause CPU spikes and livelock-like behavior where threads repeatedly fail CAS operations.

### Which Java classes implement optimistic locking?

The `java.util.concurrent.atomic` package provides optimistic implementations including `AtomicInteger`, `AtomicLong`, and `LongAdder`. For database-style versioning, you manually implement version-field checks using SQL `WHERE` clauses or application-level version counters.