Java并发编程--JUC并发工具类之StampedLock
摘要
-
本文介绍StampedLock相关技术
-
本文基于
jdk1.8
StampedLock介绍
-
如果我们深入分析
ReentrantReadWriteLock
,会发现它有个潜在的问题:如果有线程正在读,写线程需要等待读线程释放锁后才能获取写锁,即读的过程中不允许写,这是一种悲观的读锁。 -
为了进一步提升并发执行效率,Java 8引入了新的读写锁:StampedLock。
-
StampedLock
和ReentrantReadWriteLock
相比,改进之处在于:读的过程中也允许获取写锁后写入!在原先读写锁的基础上新增了一种叫乐观读(Optimistic Reading
)的模式。该模式并不会加锁,所以不会阻塞线程,会有更高的吞吐量和更高的性能。 -
它的设计初衷是作为一个内部工具类,用于开发其他线程安全的组件,提升系统性能,并且编程模型也比
ReentrantReadWriteLock
复杂,所以用不好就很容易出现死锁或者线程安全等莫名其妙的问题。 -
注意:
StampedLock
是Java 8引入的类,需要使用支持Java 8及更高版本的编译器和运行时环境。
StampLock三种访问模式
-
Writing
(独占写锁):writeLock
方法会使线程阻塞等待独占访问,可类比ReentrantReadWriteLock
的写锁模式,同一时刻有且只有一个写线程获取锁资源; -
Reading
(悲观读锁):readLock
方法,允许多个线程同时获取悲观读锁,悲观读锁与独占写锁互斥,与乐观读共享。 -
Optimistic Reading
(乐观读):这里需要注意了,乐观读并没有加锁,也就是不会有CAS
机制并且没有阻塞线程。仅当当前未处于Writing
模式tryOptimisticRead
才会返回非0
的邮戳(Stamp
),如果在获取乐观读之后没有出现写模式线程获取锁,则在方法validate
返回true
,允许多个线程获取乐观读以及读锁,同时允许一个写线程获取写锁。
乐观读编程模型的模板
1 | public void optimisticRead() { |
StampedLock的代码示例
1 | import java.util.concurrent.locks.StampedLock; |
-
在上述示例中,
StampedLockExample
类使用了StampedLock
来管理对x和y坐标的访问。 -
其中
move
方法使用写锁来更新x和y坐标的值。 -
distanceFromOrigin
方法使用乐观读锁尝试读取x
和y
坐标的值,如果乐观读锁无效,则获取悲观读锁来读取。 -
moveIfAtOrigin
方法首先获取悲观读锁,然后检查当前坐标是否在原点。如果在原点,则尝试将悲观读锁升级为写锁,以便进行坐标更新。如果升级失败,则释放悲观读锁并获取写锁。
StampedLock使用场景
-
适用于读多写少的高并发场景
使用StampedLock的注意事项
-
StampedLock
写锁是不可重入的,如果当前线程已经获取了写锁,再次重复获取的话就会死锁,使用过程中一定要注意; -
StampedLock
的悲观读、写锁都不支持条件变量Conditon
,当需要这个特性的时候需要注意; -
如果线程阻塞在
StampedLock
的readLock()
或者writeLock()
上时,此时调用该阻塞线程的interrupt()
方法,会导致 CPU 飙升。所以,使用StampedLock
一定不要调用中断操作,如果需要支持中断功能,一定使用可中断的悲观读锁readLockInterruptibly()
和写锁writeLockInterruptibly()
。
ReentrantLock 、 ReentrantReadWriteLock 和 StampedLock 的比较
特性 | ReentrantLock | ReentrantReadWriteLock | StampedLock |
---|---|---|---|
锁类型 | 独占锁(Exclusive Lock) | 读写锁(Read-Write Lock) | 乐观读锁和悲观读锁(Optimistic and Pessimistic Read Locks) |
读-读并发性 | 不支持 | 支持 | 支持 |
读-写并发性 | 不支持 | 支持 | 支持 |
写-写并发性 | 不支持 | 不支持 | 不支持 |
锁的公平性 | 支持设置为公平或非公平锁 | 支持设置为公平或非公平锁 | 不支持设置公平性(非公平锁) |
性能 | 适用于读操作少、写操作多的场景 | 适用于读操作频繁、写操作较少的场景 | 适用于乐观读多于写的场景 |
锁降级 | 不支持 | 支持 | 不支持 |
可重入性 | 支持 | 支持 | 不支持 |
API | 提供基本锁操作方法(lock、unlock、tryLock等) | 提供与ReentrantLock类似的锁操作方法,以及读锁和写锁的获取方法 | 提供乐观读锁和悲观读锁的获取方法,以及锁的转换操作 |
适用场景 | 读操作较少、写操作较多的场景,互斥访问共享资源 | 读操作频繁、写操作较少的场景,读多写少的并发访问 | 读操作多于写操作的场景,乐观读锁性能好 |