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 的正确管理。

相关推荐
丶小鱼丶几秒前
并发编程之【优雅地结束线程的执行】
java
市场部需要一个软件开发岗位5 分钟前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
忆~遂愿9 分钟前
GE 引擎进阶:依赖图的原子性管理与异构算子协作调度
java·开发语言·人工智能
MZ_ZXD00114 分钟前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
PP东16 分钟前
Flowable学习(二)——Flowable概念学习
java·后端·学习·flowable
ManThink Technology21 分钟前
如何使用EBHelper 简化EdgeBus的代码编写?
java·前端·网络
invicinble25 分钟前
springboot的核心实现机制原理
java·spring boot·后端
人道领域33 分钟前
SSM框架从入门到入土(AOP面向切面编程)
java·开发语言
大模型玩家七七1 小时前
梯度累积真的省显存吗?它换走的是什么成本
java·javascript·数据库·人工智能·深度学习
CodeToGym1 小时前
【Java 办公自动化】Apache POI 入门:手把手教你实现 Excel 导入与导出
java·apache·excel