作者 | Ressmix
来源 | https://segmentfault.com/a/1190000015808032
一、StampedLock类简介在 搞定ReentrantReadWriteLock 几道小小数学题就够了,我们详细的介绍了RWL,但 Doug Lea 觉得不够好 。StampedLock类,在JDK1.8时引入,是对读写锁ReentrantReadWriteLock的增强,该类提供了一些功能,优化了读锁、写锁的访问,同时使读写锁之间可以互相转换,更细粒度控制并发 。
首先明确下,该类的设计初衷是作为一个内部工具类,用于辅助开发其它线程安全组件,用得好,该类可以提升系统性能,用不好,容易产生死锁和其它莫名其妙的问题,算是一把“双刃剑”
1.1 StampedLock的引入
先来看下,为什么有了ReentrantReadWriteLock,还要引入StampedLock?ReentrantReadWriteLock使得多个读线程同时持有读锁(只要写锁未被占用),而写锁是独占的 。
但是,读写锁如果使用不当,很容易产生“饥饿”问题:
比如在读线程非常多,写线程很少的情况下,很容易导致写线程“饥饿”,虽然使用“公平”策略可以一定程度上缓解这个问题,但是“公平”策略是以牺牲系统吞吐量为代价的 。(在ReentrantLock类的介绍章节中,介绍过这种情况)
1.2 StampedLock的特点StampedLock的主要特点概括一下,有以下几点:
- 所有获取锁的方法,都返回一个邮戳(Stamp),Stamp为0表示获取失败,其余都表示成功;
- 所有释放锁的方法,都需要一个邮戳(Stamp),这个Stamp必须是和成功获取锁时得到的Stamp一致;
- StampedLock是不可重入的;(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)
- StampedLock有三种访问模式:①Reading(读模式):功能和ReentrantReadWriteLock的读锁类似;②Writing(写模式):功能和ReentrantReadWriteLock的写锁类似;③Optimistic reading(乐观读模式):这是一种优化的读模式 。
- StampedLock支持读锁和写锁的相互转换;我们知道RRW中,当线程获取到写锁后,可以降级为读锁,但是读锁是不能直接升级为写锁的 。StampedLock提供了读锁和写锁相互转换的功能,使得该类支持更多的应用场景 。
- 无论写锁还是读锁,都不支持Conditon等待 。
我们知道,在ReentrantReadWriteLock中,当读锁被使用时,如果有线程尝试获取写锁,该写线程会阻塞 。但是,在Optimistic reading中,即使读线程获取到了读锁,写线程尝试获取写锁也不会阻塞,这相当于对读模式的优化,但是可能会导致数据不一致的问题 。所以,当使用Optimistic reading获取到读锁时,必须对获取结果进行校验 。二、StampedLock使用示例先来看一个Oracle官方的例子:
class Point { private double x, y; private final StampedLock sl = new StampedLock(); void move(double deltaX, double deltaY) { long stamp = sl.writeLock(); //涉及对共享资源的修改,使用写锁-独占操作 try { x += deltaX; y += deltaY; } finally { sl.unlockWrite(stamp); } } /** * 使用乐观读锁访问共享资源 * 注意:乐观读锁在保证数据一致性上需要拷贝一份要操作的变量到方法栈,并且在操作数据时候可能其他写线程已经修改了数据, * 而我们操作的是方法栈里面的数据,也就是一个快照,所以最多返回的不是最新的数据,但是一致性还是得到保障的 。 * * @return */ double distanceFromOrigin() { 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); } }}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 好壶难得知己更难求,茶具知识什么是花器
- 比宇宙更古老的恒星 宇宙最古老的恒星
- 漂亮的女人离婚概率更大是为什么?
- 经常喝茶好吗,如何品茶会更健康
- 据说“新陈代谢”快的人更健康!用运动来为代谢“加速”
- 冬天严寒手脚干裂 护手霜使用不当副作用更大
- 冬季泡脚要加四样宝 养生效果会更好
- Android端微软远程桌面应用更新:全面支持Windows虚拟桌面
- 饮食|更适合老年人的饮食结构是什么?
- 失眠吃什么好 治疗更年期失眠的六款药膳粥