我们知道在ReadWriteLock
中写锁和读锁是互斥的,也就是如果有一个线程在写共享变量的话,其他线程读共享变量都会阻塞。
StampedLock
把读分为了悲观读锁和乐观读,悲观读锁就等价于ReadWriteLock
的读锁,而乐观读在一个线程写共享变量时,不会被阻塞,乐观读是不加锁的。所以没锁肯定是比有锁的性能好,这样的话在大并发读情况下效率就更高了!
StampedLock
的用法稍稍有点不同,在获取写锁和悲观读锁时,都会返回一个stamp,解锁时需要传入这个stamp,在乐观读时是用来验证共享变量是否被其他线程写过。
使用示例
实现Point
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| public 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); } }
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) { 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); } } }
|
UML
注意事项
- StampedLock 不支持重入
- StampedLock 的悲观读锁、写锁都不支持条件变量
- 如果线程阻塞在
StampedLock
的readLock()
或者writeLock()
上时,此时调用该阻塞线程的interrupt()
方法,会导致CPU飙升。所以,使用StampedLock
一定不要调用中断操作,如果需要支持中断功能,一定使用可中断的悲观读锁readLockInterruptibly()
和写锁writeLockInterruptibly()
模板代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| final StampedLock sl = new StampedLock(); // 乐观读 long stamp = sl.tryOptimisticRead();
// 读⼊⽅法局部变量 ......
// 校验 stamp if (!sl.validate(stamp)){ // 升级为悲观读锁 stamp = sl.readLock(); try { // 读⼊⽅法局部变量 ..... } finally { // 释放悲观读锁 sl.unlockRead(stamp); } }
// 使⽤⽅法局部变量执⾏业务操作 ......
long stamp = sl.writeLock(); try { // 写共享变量 ...... } finally { sl.unlockWrite(stamp); }
|