设为首页 加入收藏

TOP

StampedLock:JDK1.8中新增,比ReadWriteLock还快的锁(一)
2023-07-25 21:33:07 】 浏览:36
Tags:StampedLock JDK1.8 ReadWriteLock
摘要:StampedLock是一种在读取共享变量的过程中,允许后面的一个线程获取写锁对共享变量进行写操作,使用乐观读避免数据不一致的问题,并且在读多写少的高并发环境下,比ReadWriteLock更快的一种锁。

本文分享自华为云社区《一文彻底理解并发编程中非常重要的票据锁——StampedLock》,作者:冰 河 。

什么是StampedLock?

ReadWriteLock锁允许多个线程同时读取共享变量,但是在读取共享变量的时候,不允许另外的线程多共享变量进行写操作,更多的适合于读多写少的环境中。那么,在读多写少的环境中,有没有一种比ReadWriteLock更快的锁呢?

答案当然是有!那就是我们今天要介绍的主角——JDK1.8中新增的StampedLock!没错,就是它!

StampedLock与ReadWriteLock相比,在读的过程中也允许后面的一个线程获取写锁对共享变量进行写操作,为了避免读取的数据不一致,使用StampedLock读取共享变量时,需要对共享变量进行是否有写入的检验操作,并且这种读是一种乐观读。

总之,StampedLock是一种在读取共享变量的过程中,允许后面的一个线程获取写锁对共享变量进行写操作,使用乐观读避免数据不一致的问题,并且在读多写少的高并发环境下,比ReadWriteLock更快的一种锁。

StampedLock三种锁模式

这里,我们可以简单对比下StampedLock与ReadWriteLock,ReadWriteLock支持两种锁模式:一种是读锁,另一种是写锁,并且ReadWriteLock允许多个线程同时读共享变量,在读时,不允许写,在写时,不允许读,读和写是互斥的,所以,ReadWriteLock中的读锁,更多的是指悲观读锁。

StampedLock支持三种锁模式:写锁、读锁(这里的读锁指的是悲观读锁)和乐观读(很多资料和书籍写的是乐观读锁,这里我个人觉得更准确的是乐观读,为啥呢?我们继续往下看啊)。其中,写锁和读锁与ReadWriteLock中的语义类似,允许多个线程同时获取读锁,但是只允许一个线程获取写锁,写锁和读锁也是互斥的。

另一个与ReadWriteLock不同的地方在于:StampedLock在获取读锁或者写锁成功后,都会返回一个Long类型的变量,之后在释放锁时,需要传入这个Long类型的变量。例如,下面的伪代码所示的逻辑演示了StampedLock如何获取锁和释放锁。

public class StampedLockDemo{
 //创建StampedLock锁对象
 public StampedLock stampedLock = new StampedLock();
 //获取、释放读锁
 public void testGetAndReleaseReadLock(){
 long stamp = stampedLock.readLock();
 try{
 //执行获取读锁后的业务逻辑
 }finally{
 //释放锁
 stampedLock.unlockRead(stamp);
 }
 }
 //获取、释放写锁
 public void testGetAndReleaseWriteLock(){
 long stamp = stampedLock.writeLock();
 try{
 //执行获取写锁后的业务逻辑。
 }finally{
 //释放锁
 stampedLock.unlockWrite(stamp);
 }
 }
}

StampedLock支持乐观读,这是它比ReadWriteLock性能要好的关键所在。 ReadWriteLock在读取共享变量时,所有对共享变量的写操作都会被阻塞。而StampedLock提供的乐观读,在多个线程读取共享变量时,允许一个线程对共享变量进行写操作。

我们再来看一下JDK官方给出的StampedLock示例,如下所示。

class Point {
 private double x, y;
 private final StampedLock sl = new StampedLock();
 void move(double deltaX, double deltaY) { // an exclusively locked method
 long stamp = sl.writeLock();
 try {
            x += deltaX;
            y += deltaY;
 } finally {
 sl.unlockWrite(stamp);
 }
 }
 double distanceFromOrigin() { // A read-only method
 long stamp = sl.tryOptimisticRead();
 double currentX = x, currentY = y;
 if (!sl.validate(stamp)) {
            stamp = sl.readLock();
 try {
 currentX = x;
 currentY = y;
 } finally {
 sl.unlockRead(stamp);
 }
 }
 return Math.sqrt(currentX * currentX + currentY * currentY);
 }
 void moveIfAtOrigin(double newX, double newY) { // upgrade
 // Could instead start with optimistic, not read mode
 long stamp = sl.readLock();
 try {
 while (x == 0.0 && y == 0.0) {
 long ws = sl.tryConvertToWriteLock(stamp);
 if (ws != 0L) {
                    stamp = ws;
                    x = newX;
                    y = newY;
 break;
 }
 else {
 sl.unlockRead(stamp);
                    stamp = sl.writeLock();
 }
 }
 } finally {
 sl.unlock(stamp);
 }
 }
}

在上述代码中,如果在执行乐观读操作时,另外的线程对共享变量进行了写操作,则会把乐观读升级为悲观读锁,如下代码片段所示。

double distanceFromOrigin() { // A read-only method
 //乐观读
 long stamp = sl.tryOptimisticRead();
 double currentX = x, currentY = y;
 //判断是否有线程对变量进行了写操作
 //如果有线程对共享变量进行了写操作
 //则sl.validate(stamp)会返回false
 if (!sl.validate(stamp)) {
 //将乐观读升级为悲观读锁
        stamp = sl.readLock();
 try {
 currentX = x;
 currentY = y;
 } finally {
 //释放悲观锁
 sl.unlockRead(stamp);
 }
 }
 return Math.sqrt(currentX * currentX + currentY * currentY);
}

这种将乐观读升级为悲观读锁的方式相比一直使用乐观读的方式更加合理,如果不升级为悲观读锁,则程序会在一个循环中反复执行乐观读操作,直到乐观读操作期间没有线程执行写操作,而在循环中不断的执行乐观读会消耗大

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇基于微信授权的三方授权登陆快速.. 下一篇Spring注解系列——@PropertySour..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目