Skip to content
On this page

StampedLock

The previously introduced ReadWriteLock can solve the problem of multiple threads reading simultaneously while only one thread can write at a time.

However, upon deeper analysis of ReadWriteLock, we发现 it has a潜在问题: if there are threads currently reading, a writing thread must wait for the reading threads to release the lock before it can acquire the write lock. This means that during the reading process, writing is not allowed, which is a form of pessimistic locking.

To further enhance concurrent execution efficiency, Java 8 introduced a new read-write lock: StampedLock.

Compared to ReadWriteLock, StampedLock improves by allowing the acquisition of a write lock even during the reading process. This means that the data being read may become inconsistent, so additional code is needed to check whether any writes occurred during the read. This type of read lock is known as an optimistic lock.

An optimistic lock optimistically assumes that writes will rarely occur during reads, hence the name "optimistic lock." Conversely, a pessimistic lock rejects writes during reads, meaning that writes must wait. Clearly, optimistic locks offer higher concurrency efficiency, but if a rare write causes inconsistent reads, it needs to be detected, and the read operation can be retried.

Let's look at an example:

java
public class Point {
    private final StampedLock stampedLock = new StampedLock();

    private double x;
    private double y;

    public void move(double deltaX, double deltaY) {
        long stamp = stampedLock.writeLock(); // Acquire the write lock
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            stampedLock.unlockWrite(stamp); // Release the write lock
        }
    }

    public double distanceFromOrigin() {
        long stamp = stampedLock.tryOptimisticRead(); // Obtain an optimistic read lock
        // Note that the following two lines are not atomic operations
        // Suppose x, y = (100, 200)
        double currentX = x;
        // At this point, x=100 has been read, but x and y may have been modified by a write thread to (300, 400)
        double currentY = y;
        // At this point, y has been read. If there was no write, the read is correct (100, 200)
        // If there was a write, the read is incorrect (100, 400)
        if (!stampedLock.validate(stamp)) { // Check if any write occurred after the optimistic read
            stamp = stampedLock.readLock(); // Acquire a pessimistic read lock
            try {
                currentX = x;
                currentY = y;
            } finally {
                stampedLock.unlockRead(stamp); // Release the pessimistic read lock
            }
        }
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }
}

Compared to ReadWriteLock, the locking for writes remains the same. The difference lies in how reads are handled. Notice that we first obtain an optimistic read lock using tryOptimisticRead(), which returns a version stamp. We then perform the read operations. After reading, we use validate() to check if the version stamp is still valid, meaning no write occurred during the read. If the validation fails, we acquire a pessimistic read lock and read the data again. Since writes are infrequent, the program can obtain data through the optimistic read lock in most cases, and only a small number of instances will require acquiring a pessimistic read lock.

Thus, StampedLock subdivides the read lock into optimistic and pessimistic reads, further enhancing concurrency efficiency. However, this comes at a cost:

  1. The code becomes more complex.
  2. StampedLock is a non-reentrant lock and cannot be acquired multiple times by the same thread.

StampedLock also provides more complex functionalities, such as upgrading a pessimistic read lock to a write lock. This is mainly used in scenarios like if-then-update, where you first read, and if the read data meets certain conditions, you return; otherwise, you attempt to write.

Summary

  • StampedLock provides an optimistic read lock and can replace ReadWriteLock to further enhance concurrency performance.
  • StampedLock is a non-reentrant lock.
StampedLock has loaded