Optimistic vs Pessimistic Locking - Concurrency Without Corruption

Site Admin · 11 Sep 2026 · 8 views

Optimistic vs Pessimistic Locking - Concurrency Without Corruption

When two users try to update the same row at the same time, one update silently overwrites the other. This is called a lost update. JPA provides two strategies to prevent it: optimistic locking and pessimistic locking. Choosing the right one depends on how frequently conflicts occur and how expensive they are to resolve.

How Concurrency Conflicts Happen

Imagine two clerks fetching the same bank account. Clerk A reads balance = $1000 and prepares to deposit $200. Clerk B reads balance = $1000 and prepares to withdraw $100. If both writes execute without coordination, the final balance will be wrong - one update is lost entirely.

Thread A: reads balance = 1000
Thread B: reads balance = 1000
Thread A: writes balance = 1200  (+200 deposit)
Thread B: writes balance = 900   (-100 withdrawal)
Final DB: 900  -- Thread A update LOST

Optimistic Locking with @Version

Optimistic locking assumes conflicts are rare. You add a @Version field to your entity. Every UPDATE includes the version in the WHERE clause. If another transaction changed the row first, the version number will not match and the update affects zero rows, triggering an OptimisticLockException:

import jakarta.persistence.*;

@Entity
@Table(name = "accounts")
public class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String owner;
    private double balance;

    @Version
    private long version;

    public void deposit(double amount) {
        this.balance += amount;
    }

    // getters and setters
}

The generated SQL becomes: UPDATE accounts SET balance = ?, version = version + 1 WHERE id = ? AND version = ?. If version does not match, Hibernate throws OptimisticLockException and you catch it to retry or notify the user. No database locks are held during the read, so this is very lightweight.

Pessimistic Locking with find()

Pessimistic locking is the opposite approach: you acquire a database lock BEFORE making changes. This guarantees no other transaction can modify the row until you release the lock. Use it when conflicts are frequent or the cost of retrying is high:

@PersistenceContext
private EntityManager entityManager;

public void withdraw(Long accountId, double amount) {
    Account account = entityManager.find(
        Account.class, accountId,
        LockModeType.PESSIMISTIC_WRITE
    );
    if (account.getBalance() < amount) {
        throw new InsufficientFundsException();
    }
    account.setBalance(account.getBalance() - amount);
}

The PESSIMISTIC_WRITE lock mode generates a SELECT ... FOR UPDATE statement. The database holds a row-level lock until the transaction commits or rolls back. Other transactions that try to lock the same row will wait or time out.

Choosing Between Them

+---------------------+-----------------------+------------------------+
| Criteria            | Optimistic            | Pessimistic            |
+---------------------+-----------------------+------------------------+
| Lock held           | None (compare on write)| Database row lock      |
| Best when           | Conflicts are rare    | Conflicts are common   |
| Performance impact  | Minimal               | Blocks other threads   |
| Failure handling    | Catch exception, retry| Wait or timeout        |
| JPA annotation      | @Version              | LockModeType enum      |
+---------------------+-----------------------+------------------------+

In most web applications, optimistic locking is the better default. Use pessimistic locking for financial transfers, inventory reservations, or any scenario where a retry would produce incorrect results.

Key Points

  • Lost updates occur when two transactions read the same row and write without coordination.
  • Optimistic locking uses a @Version field and compares on UPDATE to detect conflicts.
  • Pessimistic locking acquires a database row lock (SELECT ... FOR UPDATE) before changes.
  • Optimistic locking has minimal overhead and suits most web applications.
  • Pessimistic locking is best for high-conflict scenarios like financial transfers.
  • Catch OptimisticLockException and retry the operation when using optimistic locking.
Share this post:

Comments (0)

Please login or register to comment.