StampedLock:有没有比读写锁更快的锁?
StampedLock 支持的三种锁模式
- ReadWriteLock 支持两种模式:一种是读锁,一种是写锁。而 StampedLock 支持三种模式,分别是:写锁、悲观读锁和乐观读。
- 其中,写锁、悲观读锁的语义和 ReadWriteLock 的写锁、读锁的语义非常类似,允许多个线程同时获取悲观读锁,但是只允许一个线程获取写锁,写锁和悲观读锁是互斥的。
- 不同的是:StampedLock 里的写锁和悲观读锁加锁成功之后,都会返回⼀个 stamp;然后解锁的时候,需要传入这个 stamp。
- StampedLock 的性能之所以比 ReadWriteLock 还要好,其关键是 StampedLock 支持乐观读的方式。
- ReadWriteLock 支持多个线程同时读,但是当多个线程同时读的时候,所有的写操作会被阻塞;
- StampedLock 提供的乐观读,是允许一个线程获取写锁的,也就是说不是所有的写操作都被阻塞。
- 乐观读这个操作是无锁的,所以相比较 ReadWriteLock 的读锁,乐观读的性能更好一些。
import java.util.concurrent.locks.StampedLock; public class Point { private int x; private int y; final StampedLock stampedLock = new StampedLock(); // 计算到原点的距离 private int distanceFromOrigin() { // 乐观读 long stamp = stampedLock.tryOptimisticRead(); // 读⼊局部变量,读的过程数据可能被修改 int curX = x; int curY = y; /* * 判断执⾏读操作期间,是否存在写操作,如果存在 * 则 stampedLock.validate 返回 false */ if (!stampedLock.validate(stamp)) { // 升级为悲观读锁 stamp = stampedLock.readLock(); try { curX = x; curY = y; } finally { // 释放悲观读锁 stampedLock.unlockRead(stamp); } } return (int) Math.sqrt(curX * curX + curY * curY); } }
StampedLock 使用注意事项
- 对于读多写少的场景 StampedLock 性能很好,简单的应用场景基本上可以替代 ReadWriteLock,但是 StampedLock 的功能仅仅是 ReadWriteLock 的子集,在使用的时候,还是有几个地方需要注意一下。
- StampedLock 不支持重入。
- 另外,StampedLock 的悲观读锁、写锁都不支持条件变量。
- 如果线程阻塞在 StampedLock 的 readLock() 或者 writeLock() 上时,此时调用该阻塞线程的 interrupt() 方法,会导致 CPU 飙升。
- 使用 StampedLock 一定不要调用中断操作,如果需要支持中断功能,一定使用可中断的悲观读锁 readLockInterruptibly() 和写锁 writeLockInterruptibly()。