java并发编程之美 学习笔记
StampedLock
StampedLock
是jdk8之后新增的一个锁,该锁提供了三种模式的读写控制。
当调用获取锁的函数时,会返回一个long型变量,我们称之为戳记(stamp)
,这个戳记代表了锁的状态。
tryXXXLock
:获取锁失败返回的stamp=0
.在释放锁unlock(stamp)
需要将获取锁时的stamp传入.
StampedLock提供了三种读写模式的锁:
- 写锁writeLock: 排他独占锁.(
不可重入
) - 悲观读锁readLock:共享锁(
不可重入
). 这里说的悲观是指,在操作数据之前会悲观的认为其他线程可能对自己操作的数据进行修改,所以要先对数据加锁,这是在读少写多
的情况下的一种考虑。 - 悲观读锁readLock: 它是相对于悲观来说的,在操作数据之前,并
没有通过
CAS设置锁的状态。- 如果当前线程没有持有写锁,则简单的返回一个
非0的stamp
- 获取stamp之后,在操作具体的数据之前还需要
validate(stamp)
校验stamp是否依然可用(即在持有锁的时间段内,有没有其他线程持有了写锁
),如果有则validate返回0,表示不可用。 否则返回一个非0,表示可用。该锁适用于读多写少
的应用场景。
- 如果当前线程没有持有写锁,则简单的返回一个
锁转换
StampedLock还支持三种锁在一定情况下互相转换,如tryConvertToWriteLock(stamp)
,期望把stamp标示的锁升级为写锁,但必须满足如下条件中的一个:
- 当前锁已经处于写模式
- 当前锁处于读模式,但是没有其他线程持有读锁。
- 当前处于乐观读模式,目前当前写锁可用。
例
public class StampedLockTest {
//StampedLock 的读写锁都是不可重入锁.
private StampedLock lock = new StampedLock();
private double x ,y;
//写锁
void move(double deltaX , double deltaY) {
//获取写锁 --- blocking 直到有可用的写锁
long stamp = lock.writeLock();
try{
x += deltaX;
y += deltaY;
} finally {
lock.unlockWrite(stamp);
}
}
//乐观读锁
double distanceFromOrigin(){
//获取乐观锁 --- 立即返回一个状态,非0表示可用;
long stamp = lock.tryOptimisticRead();
double currentX = x ,currentY = y;
//检验锁有没有被其他写线希呈排它性抢占
if( !lock.validate(stamp)){
stamp = lock.readLock();
try{
currentX = x;
currentY = y;
}finally {
lock.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
// 使用悲观锁获取读锁,并尝试转换为写锁
void moveIfOrigin(double newX ,double newY) {
//获取悲观读锁 --- blocking 直到有可用的读锁
long stamp = lock.readLock();
try{
while( x == 0.0 && y == 0.0){
//尝试将获取的读锁升级为 写锁
long ws = lock.tryConvertToWriteLock(stamp);
//升级成功 ,则 更新戳记,并设置坐标值,然后退出循环
if(ws != 0 ){
stamp = ws;
x = newX;
y = newY;
break;
}else{
//读锁升级写锁失败则释放读锁,显式获取独占写锁,然后循环重试
lock.unlockRead(stamp);
stamp = lock.writeLock();
}
}
} finally {
lock.unlock(stamp);
}
}
}