上一篇我们学习了StampedLock邮戳锁引入背景以及邮戳锁的三种锁模式、邮戳锁的特性以及邮戳锁的核心api。本来就来进一步学习邮戳锁的具体使用。
首先我们先来分析官方提供的实例,分析StampedLock的主要api方法的使用方法。官方提供的例子是多线程环境下更新点的坐标并计算到原点的距离,这个功能在日常的业务开发中并不是很常见,但不妨碍我们对相关api使用的了解。
多线程更新坐标并计算到原点的距离
核心需求分析
1、线程安全的更新坐标值
- 写操作需独占锁保证原子性,防止并发修改导致数据错乱
- 使用
writeLock()获取写锁,更新后通过unlockWrite(stamp)释放写锁。
2、高效的距离计算
- 优先采用乐观读(
tryOptimisticRead)减少锁竞争,以便适合高频读取的场景。 - 若检测到写冲突(
validate(stamp)失败),降级为悲观读锁重新获取数据。
3、数据一致性保证
- 乐观读需拷贝共享变量到局部变量,避免验证后数据被修改。
- 悲观读锁与写锁互斥,确保强一致性但可能降低吞吐量。
代码实现
我们对官方提供的例子稍微做了改造和备注,如下:
public class Point {
// 坐标点
private static double x, y;
// 创建邮戳锁
private static final StampedLock sl = new StampedLock();
// 一个独占锁方法(写锁是独占的)
public static void move(double deltaX, double deltaY) {
// 获取写锁
long stamp = sl.writeLock();
try {
// 修改坐标
x += deltaX;
y += deltaY;
System.out.printf("Thread %s moved to (%.1f, %.1f)%n", Thread.currentThread().getName(), x, y);
} finally {
// 根据邮戳释放写锁
sl.unlockWrite(stamp);
}
}
// 一个只读的方法
// 从乐观读锁升级为读锁(悲观读)
public static double distanceFromOrigin() {
// 尝试乐观读锁,返回非0的stamp表示当前无写锁
long stamp = sl.tryOptimisticRead();
try {
retryHoldingLock:
// 获取共享读锁
for (; ; stamp = sl.readLock()) {
// 存在写锁则重试获取写锁
if (stamp == 0L)
continue retryHoldingLock;
// 尽可能单纯的读操作
double currentX = x;
double currentY = y;
//检查乐观读期间是否有写操作,返回true表示数据有效,若返回false,需升级为悲观读或重试
if (!sl.validate(stamp))
continue retryHoldingLock;
//计算二维平面上点 (x, y) 到原点 (0, 0)
double distance = Math.hypot(currentX, currentY);
System.out.printf("Thread %s calculated distance: %.2f%n", Thread.currentThread().getName(), distance);
return distance;
}
} finally {
//判断邮戳代表的是否是读锁
//if (StampedLock.isReadLockStamp(stamp))
if (isReadLockStamp(stamp))
//释放读锁
sl.unlockRead(stamp);
}
}
// 乐观读锁升级为写锁
public static void moveIfAtOrigin(double newX, double newY) {
long stamp = sl.tryOptimisticRead();
try {
retryHoldingLock:
for (; ; stamp = sl.writeLock()) {
if (stamp == 0L)
continue retryHoldingLock;
double currentX = x;
double currentY = y;
if (!sl.validate(stamp))
continue retryHoldingLock;
if (currentX != 0.0 || currentY != 0.0)
break;
//尝试将读锁升级为写锁,返回新stamp或0。
stamp = sl.tryConvertToWriteLock(stamp);
if (stamp == 0L)
//升级失败重新获取写锁。
continue retryHoldingLock;
// 排他访问
x = newX;
y = newY;
return;

最低0.47元/天 解锁文章
996

被折叠的 条评论
为什么被折叠?



