本文分享自华为云社区《【高并发】一文彻底理解并发编程中非常重要的票据锁——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 methodlong stamp = sl.writeLock();try {x += deltaX;y += deltaY;} finally {sl.unlockWrite(stamp);}}double distanceFromOrigin() { // A read-only methodlong 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 modelong 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)会返回falseif (!sl.validate(stamp)) {//将乐观读升级为悲观读锁stamp = sl.readLock();try {currentX = x;currentY = y;} finally {//释放悲观锁sl.unlockRead(stamp);}}return Math.sqrt(currentX * currentX + currentY * currentY);}
推荐阅读
- 长的胖的女明星有哪些,女明星胖的有谁-
- 口腔内溃烂
- 林雨申|《飞狐外传》林雨申太帅了,风头盖过男主,但也有一个明显的槽点
- 李世民有几任皇后,李世民登基后的第一个皇后是谁-
- 成都|成都十年艰难故事:我的一个外企同学
- 刘晓庆|刘晓庆和妹妹家中聚会,同穿红色韵味大不同一个美艳一个温婉
- 古羌人特征,古羌人起源地-
- 长衫老人的品质,老人与海老人的品质-
- 泸州特产能带走的
- 西西里的美丽传说|电影《西西里的美丽传说》中的西西里是怎样一个故事?