StampedLock and optimistic reads

Multithreading · lesson 34 of 38 · 4 min read

Read shared state with no lock at all, then check whether a writer got in while you read.

Open this lesson in the learning hub

Key points

  • tryOptimisticRead() takes no lock. Read the fields into locals, then validate(stamp) asks if a writer got in.
  • If validation fails, retry or fall back to readLock(). Copy into locals first, or you may compute on torn values.
  • It beats ReentrantReadWriteLock when reads dominate: an uncontended optimistic read costs about one volatile read.
  • StampedLock is not reentrant and has no Condition. Taking it twice on one thread deadlocks that thread.
  • Unlock with the exact stamp you were given, always in a finally. tryConvertToWriteLock upgrades in place.

Example

import java.util.concurrent.locks.StampedLock;

public class Main {
    static final StampedLock lock = new StampedLock();
    static double x = 3.0, y = 4.0;
    static int optimisticWins = 0, fallbacks = 0;

    // The documented optimistic-read idiom: no lock, copy the fields, then validate.
    static double distance() {
        long stamp = lock.tryOptimisticRead();       // takes no lock at all
        double cx = x, cy = y;                       // copy into locals first
        if (lock.validate(stamp)) {                  // did any writer get in?
            optimisticWins++;
            return Math.sqrt(cx * cx + cy * cy);
        }
        stamp = lock.readLock();                     // it did, so pay for a real read lock
        try {
            fallbacks++;
            cx = x; cy = y;
        } finally { lock.unlockRead(stamp); }
        return Math.sqrt(cx * cx + cy * cy);
    }

    static void move(double dx, double dy) {
        long stamp = lock.writeLock();               // writers are exclusive
        try { x += dx; y += dy; } finally { lock.unlockWrite(stamp); }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.printf("start distance : %.2f%n", distance());

        Thread writer = new Thread(() -> { for (int i = 0; i < 300_000; i++) move(0.00001, 0.00001); });
        writer.start();
        for (int i = 0; i < 300_000; i++) distance(); // read hard while a writer is active
        writer.join();

        System.out.printf("final distance : %.2f%n", distance());
        System.out.println("optimistic ok  : " + optimisticWins + " reads with no lock");
        System.out.println("fell back      : " + fallbacks + " reads a writer interrupted");
    }
}

Optimistic read first, validate second, and lock only when validation fails.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Multithreading course, and every lesson in it is listed on the Multithreading contents page.