StampedLock
StampedLock的优势
ReentrantLock 未能读写分离实现,虽然ReentrantReadWriteLock能够读写分离了,但是对于其写锁想要获取的话,就必须没有任何其他读写锁存在才可以,这实现了悲观读取。而且如果读操作很多,写很少的情况下,线程有可能遭遇饥饿问题。
饥饿问题:ReentrantReadWriteLock实现了读写分离,想要获取读锁就必须确保当前没有其他任何读写锁了,但是一旦读操作比较多的时候,想要获取写锁就变得比较困难了,因为当前有可能会一直存在读锁。而无法获得写锁。
这时候怎么办呢?于是在jdk1.8的时候引入了一个新的锁StampedLock。
使用
StampedLock控制锁有三种模式(写,读,乐观读)(基于CLH锁)
(1)写入(Writing):writeLock是一个独占锁,也是一个悲观锁。
(2)读取(Reading):readLock这时候是一个悲观锁。
(3)乐观读取(Optimistic Reading):提供了tryOptimisticRead方法返回一个非0的stamp,只有当前同步状态没有被写模式所占有是才能获取到。乐观读取模式仅用于短时间读取操作时经常能够降低竞争和提高吞吐量。
同时使用的时候一般需要读取并存储到另外一个副本,以用做对比使用。下面干脆使用代码来实现一下这几种锁的实现。
官方示例:
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() { // A read-only method
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 moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
break;
}
else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}
StampedLock的调度策略对待读写操作都是公平合理的。所有try方法都是尽最大努力,调用可能会成功,也可能会失败。这个类没有直接实现Lock或者ReadWriteLock方法,源码中是把他当作一个单独的类来实现的。
当然,一个StampedLock可以通过asReadLock,asWriteLock,asReadWriteLock方法来得到全部功能的子集。