读多写少的场景中,还有没有更快的技术方案,Java 在 1.8 这个版本里,提供了一种叫 StampedLock 的锁,它的性能就比读写锁还要好。
StampedLock 三种锁模式
写锁,悲观读锁
写锁和悲观读锁为允许多个线程同时获取悲观锁,但只允许一个线程获取写锁,在获取锁后会返回一个stamp,解锁的时候传入stamp
final StampedLock sl = new StampedLock();
// 获取/释放悲观读锁示意代码
long stamp = sl.readLock();
try {
//省略业务相关代码
} finally {
sl.unlockRead(stamp);
}
// 获取/释放写锁示意代码
long stamp = sl.writeLock();
try {
//省略业务相关代码
} finally {
sl.unlockWrite(stamp);
}
乐观读
在多个线程同时读的时候,允许一个线程获取读锁,不是所有的写操作都被阻塞
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 注意事项
- 不支持重入
- 悲观读锁和写锁都不支持条件变量
- 如果线程阻塞在readLock和writeLock,此时调用该线程的interrupt,会导致CPU飙升
- 如果要支持中断功能,可中断的悲观读锁readLockInterruptibly()和写锁的writeLockInterruptibly()
