StampedLock
是 Java 8 中引入的一种乐观读、悲观写的锁机制。它是 ReentrantReadWriteLock
的增强版本,主要用于解决读多写少的场景,提供了更高的并发性能。StampedLock
通过引入 stamp(戳)的概念,实现了更灵活的锁控制。下面我们将深入了解 StampedLock
的特性、用法以及适用场景。
特性和设计思想:
- 三种模式:
- 写模式(writeLock): 独占锁,用于写操作。在写模式下,所有的读和写都被阻塞。
- 读模式(readLock): 共享锁,用于读操作。多个线程可以同时获得读锁,但写操作会被阻塞。
- 乐观读模式(tryOptimisticRead): 一种特殊的读模式,不会阻塞其他线程的写操作。
- 戳(stamp):
StampedLock
引入了一个名为 stamp 的概念,它是一个 long 类型的数字。- 在读锁和写锁的操作中,都会返回一个 stamp,可以用来标识锁的版本。
- 在乐观读模式下,可以通过
tryOptimisticRead
获得一个 stamp,用于检查在获取锁期间是否有写操作发生。
- 乐观读和悲观读的转换:
- 可以在乐观读模式下,通过
validate
方法检查锁是否被其他线程获取,然后转换为悲观读锁。
使用示例:
下面是一个简单的示例,演示了 StampedLock
的基本用法:
importjava.util.concurrent.locks.StampedLock; classPoint { privatedoublex, y; privatefinalStampedLocklock=newStampedLock(); // 写操作voidmove(doubledeltaX, doubledeltaY) { longstamp=lock.writeLock(); try { x+=deltaX; y+=deltaY; } finally { lock.unlockWrite(stamp); } } // 乐观读操作doubledistanceFromOrigin() { longstamp=lock.tryOptimisticRead(); doublecurrentX=x, currentY=y; if (!lock.validate(stamp)) { stamp=lock.readLock(); try { currentX=x; currentY=y; } finally { lock.unlockRead(stamp); } } returnMath.sqrt(currentX*currentX+currentY*currentY); } }
在上述示例中,move
方法使用写锁进行坐标的更新,distanceFromOrigin
方法使用乐观读锁计算点到原点的距离。在乐观读模式下,先获取一个 stamp,然后检查锁是否被其他线程获取,如果没有则直接使用数据,否则转换为悲观读锁。
适用场景:
- 读多写少的场景:
StampedLock
的优势在于读操作不会被阻塞,适用于读多写少的场景。- 在高并发读操作的情况下,
StampedLock
的性能相对于传统的ReentrantReadWriteLock
有明显提升。
- 乐观读的场景:
- 当读操作远远超过写操作时,乐观读模式可以提供更高的并发性能。
- 乐观读不会阻塞其他读操作,但需要在后续的悲观读操作中进行验证。
注意事项和限制:
- 不支持条件变量:
StampedLock
不支持条件变量,因此在使用时需要注意。
- 不可重入:
StampedLock
不支持可重入性,即同一个线程在没有释放锁的情况下无法再次获得锁。
- 悲观读锁升级为写锁:
- 悲观读锁可以升级为写锁,但需要小心死锁的问题。
- 读锁和写锁不能同时持有:
- 与
ReentrantReadWriteLock
不同,StampedLock
不允许读锁和写锁同时被持有。