邮戳锁- StampedLock

ReentrantLock 不管公平锁还是非公平锁都是独占锁,所以有了 ReentrantReadWriteLock 读写分离的锁

ReentrantReadWriteLock 性能已经很好了,读读可以并发,但是还不够快,这就是 StampedLock 的产生背景

StampedLock 还可以乐观读进一步提升性能,乐观读就是无锁的状态了

本章只分析 StampedLock,AQS 从入门到精通

使用示例

import java.util.concurrent.locks.StampedLock;

public class StampedLockExample {
    private int sharedData = 0; // 保护的数据
    private final StampedLock lock = new StampedLock();

    // 乐观读(不阻塞,需要验证)
    public int optimisticRead() {
        long stamp = lock.tryOptimisticRead(); // 乐观读
        int currentValue = sharedData; 
        if (!lock.validate(stamp)) { // 如果验戳失败,要升级为读锁
            stamp = lock.readLock(); // 锁升级
            try {
                currentValue = sharedData; // 要重新读取一次数据,防止数据已经被修改
            } finally {
                lock.unlockRead(stamp); // 释放锁(戳)
            }
        }
        return currentValue;
    }

    // 共享读(阻塞式)
    public int sharedRead() {
        long stamp = lock.readLock();
        try {
            return sharedData;
        } finally {
            lock.unlockRead(stamp); // 释放锁(戳)
        }
    }

    // 悲观写(独占锁)
    public void pessimisticWrite(int newValue) {
        long stamp = lock.writeLock();
        try {
            sharedData = newValue;
        } finally {
            lock.unlockWrite(stamp); // 释放锁(戳)
        }
    }

    public static void main(String[] args) {
        StampedLockExample example = new StampedLockExample();

        // 写线程
        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                example.pessimisticWrite(i);
                System.out.println("写入数据: " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();

        // 读线程(乐观读)
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                int value = example.optimisticRead();
                System.out.println("乐观读取: " + value);
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();

        // 读线程(共享读)
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                int value = example.sharedRead();
                System.out.println("共享读取: " + value);
                try {
                    Thread.sleep(400);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }).start();
    }
}

缺点

  • 不支持条件变量,写锁也不支持
  • 不可重入

适用场景

StampedLock 最适合读多写少的场景,特别是当:

  • 读操作远多于写操作
  • 读操作可以容忍短暂的数据不一致(乐观读)
  • 需要比 ReentrantReadWriteLock 更高的吞吐量
posted @ 2023-05-24 16:05  CyrusHuang  阅读(38)  评论(0)    收藏  举报