并发编程 StampedLock
本文将介绍并发编程中的 StampedLock,可以视为改进的读写锁。
一、适用场景
读多写少场景,且性能优于 ReadWriteLock。
二、三种模式
StampedLock 提供了三种模式,分别是:
写锁:类似于读写锁的写锁,区别在于加锁时会返回一个标记,解锁时应该传入该标记
1
2
3StampedLock stampedLock = new StampedLock();
long stamp = stampedLock.writeLock();
stampedLock.unlockWrite(stamp);悲观读锁:类似于读写锁的写锁,区别在于加锁时会返回一个标记,解锁时应该传入该标记
1
2
3StampedLock stampedLock = new StampedLock();
long stamp = stampedLock.readLock();
stampedLock.unlockRead(stamp);乐观读:不加锁的读;StampedLock 性能优于 ReadWriteLock 的关键
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21private int i = 0;
private StampedLock stampedLock = new StampedLock();
public int getI() {
// 乐观读
long stamp = stampedLock.tryOptimisticRead();
// 获取资源
int tempI = i;
// 判断此期间是否有写锁获取
if (!stampedLock.validate(stamp)) {
// 升级为悲观读锁
stamp = stampedLock.readLock();
try {
tempI = i;
} finally {
stampedLock.unlockRead(stamp);
}
}
return tempI;
}- 通过
tryOptimisticRead()
表示将要进行乐观读,同时获取当前的标记 - 访问资源
- 通过
validate(stamp)
方法判断执行tryOptimisticRead()
至今是否曾被获取写锁,- 如果有,升级为悲观读锁,读取最新数据
- 如果没有,无需额外操作
- 通过
其中,
- 悲观读锁的做法是:每次要读取资源时,首先获取读锁,在此期间禁止写锁的获取
- 乐观读的做法是:每次要读取资源时,获取一个标记,读取结束后,通过标记判断资源是否在此期间被修改
相比较而言,乐观读具有更好的性能。
三、注意事项
StampedLock 并不支持重入
StampedLock 的写锁、悲观读锁并不支持条件
ReadWriteLock 的
readLock()
和writeLock()
方法均会返回一个 Lock 实例,可以通过该实例获取条件;StampedLock 的
readLock()
和writeLock()
方法会在调用时直接加锁,并且只会返回一个标记,因此无法获取条件使用 StampedLock 时不要调用中断操作
如果线程堵塞在 StampedLock 的
readLock()
和writeLock()
方法上时,调用该线程的interrupt()
,将会导致 CPU 飙升。如果需要支持中断操作,可以使用
readLockInterruptibly()
和writeLockInterruptibly()
方法。
参考
- Java 并发编程实战