java StampedLock 的使用

StampedLock 是 Java 8 引入的一种改进的读写锁,它提供了三种模式(写锁、悲观读锁、乐观读),并且性能比 ReentrantReadWriteLock 更好。下面是详细的使用指南:

1. 三种锁模式

a) 写锁 (Write Lock)

  • 独占锁,类似 ReentrantReadWriteLock 的写锁
  • 使用 writeLock() 获取,返回一个 stamp
  • 必须使用对应的 stamp 解锁

b) 悲观读锁 (Pessimistic Read Lock)

  • 共享锁,类似 ReentrantReadWriteLock 的读锁
  • 使用 readLock() 获取
  • 阻塞写锁,不阻塞其他读锁

c) 乐观读 (Optimistic Read)

  • 无锁操作,不阻塞任何锁
  • 使用 tryOptimisticRead() 获取 stamp
  • 需要验证数据是否被修改

2. 基本使用示例

java 复制代码
import java.util.concurrent.locks.StampedLock;

public class StampedLockExample {
    private double x, y;
    private final StampedLock sl = new StampedLock();

    // 写操作 - 移动点
    void move(double deltaX, double deltaY) {
        long stamp = sl.writeLock();  // 获取写锁
        try {
            x += deltaX;
            y += deltaY;
        } finally {
            sl.unlockWrite(stamp);  // 释放写锁
        }
    }

    // 悲观读操作 - 计算距离
    double distanceFromOrigin() {
        long stamp = sl.readLock();  // 获取悲观读锁
        try {
            return Math.sqrt(x * x + y * y);
        } finally {
            sl.unlockRead(stamp);  // 释放读锁
        }
    }

    // 乐观读操作 - 尝试读取
    void moveIfAtOrigin(double newX, double newY) {
        // 乐观读(不获取锁)
        long stamp = sl.tryOptimisticRead();
        double currentX = x;
        double currentY = y;
        
        // 验证期间是否有写操作
        if (!sl.validate(stamp)) {
            // 如果验证失败,升级为悲观读锁
            stamp = sl.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                sl.unlockRead(stamp);
            }
        }
        
        // 业务逻辑:如果位于原点则移动
        if (currentX == 0.0 && currentY == 0.0) {
            long writeStamp = sl.writeLock();
            try {
                if (x == 0.0 && y == 0.0) {
                    x = newX;
                    y = newY;
                }
            } finally {
                sl.unlockWrite(writeStamp);
            }
        }
    }
}

3. 高级特性示例

a) 尝试获取锁

java 复制代码
public class StampedLockAdvanced {
    private final StampedLock lock = new StampedLock();
    private String data = "initial";

    // 尝试获取写锁(非阻塞)
    public boolean tryWrite(String newData) {
        long stamp = lock.tryWriteLock();
        if (stamp != 0L) {
            try {
                data = newData;
                return true;
            } finally {
                lock.unlockWrite(stamp);
            }
        }
        return false;
    }

    // 尝试获取读锁(带超时)
    public String tryReadWithTimeout() throws InterruptedException {
        long stamp = lock.tryReadLock(1, java.util.concurrent.TimeUnit.SECONDS);
        if (stamp != 0L) {
            try {
                return data;
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return "timeout";
    }

    // 锁升级(读锁 -> 写锁)
    public boolean upgradeReadToWrite(String newData) {
        long stamp = lock.readLock();
        try {
            // 检查条件
            if (data.equals("upgrade")) {
                // 尝试升级为写锁
                long writeStamp = lock.tryConvertToWriteLock(stamp);
                if (writeStamp != 0L) {
                    stamp = writeStamp;  // 升级成功
                    data = newData;
                    return true;
                } else {
                    // 升级失败,释放读锁,重新获取写锁
                    lock.unlockRead(stamp);
                    stamp = lock.writeLock();
                    data = newData;
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock(stamp);  // 统一解锁
        }
    }
}

b) 乐观读模式的最佳实践

java 复制代码
public class Point {
    private double x, y;
    private final StampedLock sl = new StampedLock();

    // 使用乐观读的最佳实践
    double distanceFromOriginOptimistic() {
        // 第一次尝试:乐观读
        long stamp = sl.tryOptimisticRead();
        double currentX = x, currentY = y;
        
        // 验证乐观读期间是否有写操作
        if (!sl.validate(stamp)) {
            // 验证失败,升级为悲观读锁
            stamp = sl.readLock();
            try {
                currentX = x;
                currentY = y;
            } finally {
                sl.unlockRead(stamp);
            }
        }
        
        return Math.sqrt(currentX * currentX + currentY * currentY);
    }

    // 复合操作示例
    void moveIfAt(double oldX, double oldY, double newX, double newY) {
        // 重试机制
        for (int attempt = 0; attempt < 3; attempt++) {
            long stamp = sl.readLock();
            try {
                // 检查当前值
                if (x == oldX && y == oldY) {
                    long writeStamp = sl.tryConvertToWriteLock(stamp);
                    if (writeStamp != 0L) {
                        // 转换成功
                        stamp = writeStamp;
                        x = newX;
                        y = newY;
                        return;
                    }
                }
            } finally {
                sl.unlock(stamp);
            }
            
            // 短暂休眠后重试
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            }
        }
    }
}

4. 缓存实现示例

java 复制代码
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.StampedLock;

public class StampedLockCache<K, V> {
    private final Map<K, V> map = new HashMap<>();
    private final StampedLock lock = new StampedLock();

    // 写操作
    public void put(K key, V value) {
        long stamp = lock.writeLock();
        try {
            map.put(key, value);
        } finally {
            lock.unlockWrite(stamp);
        }
    }

    // 读操作(悲观读)
    public V get(K key) {
        long stamp = lock.readLock();
        try {
            return map.get(key);
        } finally {
            lock.unlockRead(stamp);
        }
    }

    // 读操作(乐观读) - 适合读多写少的场景
    public V getOptimistic(K key) {
        // 尝试乐观读
        long stamp = lock.tryOptimisticRead();
        V value = map.get(key);
        
        if (!lock.validate(stamp)) {
            // 如果被修改,升级为悲观读
            stamp = lock.readLock();
            try {
                value = map.get(key);
            } finally {
                lock.unlockRead(stamp);
            }
        }
        return value;
    }

    // 条件更新
    public boolean updateIfPresent(K key, V newValue) {
        long stamp = lock.readLock();
        try {
            if (map.containsKey(key)) {
                long writeStamp = lock.tryConvertToWriteLock(stamp);
                if (writeStamp != 0L) {
                    stamp = writeStamp;
                    map.put(key, newValue);
                    return true;
                } else {
                    // 转换失败,释放读锁,获取写锁
                    lock.unlockRead(stamp);
                    stamp = lock.writeLock();
                    if (map.containsKey(key)) {
                        map.put(key, newValue);
                        return true;
                    }
                }
            }
        } finally {
            lock.unlock(stamp);
        }
        return false;
    }
}

5. 使用注意事项

优点:

  1. 性能更好:乐观读模式在读多写少的场景下性能优异
  2. 避免死锁:锁不可重入(但可以通过记录 stamp 实现类似效果)
  3. 灵活的锁转换:支持读锁到写锁的转换

注意事项:

  1. 不可重入:同一个线程不能重复获取锁(会死锁)
  2. Stamp 管理:必须正确保存和验证 stamp
  3. 中断处理:不支持锁获取时的中断
  4. 条件变量:没有内置的条件变量支持
  5. 避免长时间持有锁:写锁会阻塞所有读操作

最佳实践:

java 复制代码
// 模板模式:确保锁的正确释放
public class LockTemplate {
    public static void withWriteLock(StampedLock lock, Runnable task) {
        long stamp = lock.writeLock();
        try {
            task.run();
        } finally {
            lock.unlockWrite(stamp);
        }
    }
    
    public static <T> T withReadLock(StampedLock lock, Supplier<T> supplier) {
        long stamp = lock.readLock();
        try {
            return supplier.get();
        } finally {
            lock.unlockRead(stamp);
        }
    }
}

StampedLock 在读多写少的高并发场景下性能优势明显,但需要注意它的不可重入性和 stamp 的正确管理。

相关推荐
CryptoRzz2 小时前
如何高效对接美股实时行情?StockTV API 实战集成指南
java·python·flask·区块链·maven·symfony
MonkeyKing_sunyuhua2 小时前
ES文档序号写错的问题的修复
java·数据库·elasticsearch
小豪GO!2 小时前
Spring-八股
java·spring boot·spring
王干脆3 小时前
ConcurrentHashMap禁止null键值的原因
java·开发语言
牧小七3 小时前
Java注解(Annotation)全面学习指南
java
开开心心就好3 小时前
PDF密码移除工具,免费解除打印编辑复制权限
java·网络·windows·websocket·pdf·电脑·excel
咕噜企业分发小米3 小时前
豆包大模型在药物研发中的知识检索效率如何?
java·开发语言·数据库
Remember_9933 小时前
【LeetCode精选算法】二分查找专题一
java·数据结构·算法·spring·leetcode·哈希算法
BlockChain8883 小时前
Web3 后端面试专用版
java·面试·职场和发展·go·web3