StampedLock:有没有比读写锁更快的锁?
在读多写少的场景中,StampedLock比ReadWriteLock性能要好。
Why?
StampedLock 支持的三种锁模式
ReadWriteLock 支持两种模式:一种是读锁,一种是写锁。而 StampedLock 支持三种模 式,分别是:写锁、悲观读锁和乐观读。StampedLock 的性能之所以比 ReadWriteLock 还要好,其关键是 StampedLock 支持乐观读的方式。ReadWriteLock 支持多个线程同时读,但是当多个线程同时读的时候,所有的写操作会被阻塞;而 StampedLock 提供的乐观读,是允许一个线程获取写锁的,也就是说不是所有的写操作都被阻塞。
注意这里,我们用的是“乐观读”这个词,而不是“乐观读锁”,是要提醒你,乐观读这个操作是无锁的,所以相比较 ReadWriteLock 的读锁,乐观读的性能更好一些。
How?
class Point {
private int x, y;
final StampedLock sl =
new StampedLock();
// 计算到原点的距离
int distanceFromOrigin() {
// 乐观读
long stamp =
sl.tryOptimisticRead();
// 读入局部变量,
// 读的过程数据可能被修改
int curX = x, curY = y;
// 判断执行读操作期间,
// 是否存在写操作,如果存在,
// 则 sl.validate 返回 false
if (!sl.validate(stamp)){
// 升级为悲观读锁
stamp = sl.readLock();
try {
curX = x;
curY = y;
} finally {
// 释放悲观读锁
sl.unlockRead(stamp);
}
}
//使用方法局部变量执行业务逻辑
return Math.sqrt(
curX * curX + curY * curY);
}
}
StampedLock 使用注意事项
对于读多写少的场景 StampedLock 性能很好,简单的应用场景基本上可以替代 ReadWriteLock,但是StampedLock 的功能仅仅是 ReadWriteLock 的子集,在使用的 时候,还是有几个地方需要注意一下。
1 StampedLock 不支持重入。
2 StampedLock 的悲观读锁、写锁都不支持条件变量。
3 使用 StampedLock 一定不要调用中断操作,如果需要支持中断功能,一定使用可 中断的悲观读锁 readLockInterruptibly() 和写锁 writeLockInterruptibly()。
锁的升级和降级
StampedLock 支持锁的降级(通过 tryConvertToReadLock() 方法实现)和升级(通过 tryConvertToWriteLock() 方法实现),但使用时要慎重。
下面的代码也源自 Java 的官方示例
private double x, y;
final StampedLock sl = new StampedLock();
void moveIfAtOrigin(double newX, double newY){
long stamp = sl.readLock();
try {
while(x == 0.0 && y == 0.0){
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
x = newX;
y = newY;
//如果升级锁成功,tryConvertToWriteLock(stamp)这个方法内部会释放悲观读锁stamp。但是写锁需要主动释放。
stamp = ws;
break;
} else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}