悲观读/写锁示例
final StampedLock sl = new StampedLock();// 获取 / 释放悲观读锁示意代码
long stamp = sl.readLock();
try {// 省略业务相关代码
} finally {sl.unlockRead(stamp);
}// 获取 / 释放写锁示意代码
long stamp = sl.writeLock();
try {// 省略业务相关代码
} finally {sl.unlockWrite(stamp);
}
乐观读失效,升级为悲观读
public class Point {private int x, y;final StampedLock sl = new StampedLock();// 计算到原点的距离 int distanceFromOrigin() {// 乐观读long stamp = sl.tryOptimisticRead();// 读入局部变量,// 读的过程数据可能被修改int curX = x, curY = y;// 判断执行读操作期间,// 是否存在写操作,如果存在,// 则 sl.validate 返回 falseif (!sl.validate(stamp)){// 升级为悲观读锁stamp = sl.readLock();try {curX = x;curY = y;} finally {// 释放悲观读锁sl.unlockRead(stamp);}}return Math.sqrt(curX * curX + curY * curY);}
}
有一点需要特别注意,那就是:如果线程阻塞在 StampedLock 的 readLock() 或者 writeLock() 上时,此时调用该阻塞线程的 interrupt() 方法,会导致 CPU 飙升。例如下面的代码中,线程 T1 获取写锁之后将自己阻塞,线程 T2 尝试获取悲观读锁,也会阻塞;如果此时调用线程 T2 的 interrupt() 方法来中断线程 T2 的话,你会发现线程 T2 所在 CPU 会飙升到 100%。
final StampedLock lock= new StampedLock();
Thread T1 = new Thread(()->{// 获取写锁lock.writeLock();// 永远阻塞在此处,不释放写锁LockSupport.park();
});
T1.start();
// 保证 T1 获取写锁
Thread.sleep(100);Thread T2 = new Thread(()->// 阻塞在悲观读锁lock.readLock()
);
T2.start();
// 保证 T2 阻塞在读锁
Thread.sleep(100);
// 中断线程 T2
// 会导致线程 T2 所在 CPU 飙升
T2.interrupt();
T2.join();
StampedLock 读模板:
final StampedLock sl = new StampedLock();// 乐观读
long stamp = sl.tryOptimisticRead();
// 读入方法局部变量
......
// 校验 stamp
if (!sl.validate(stamp)){// 升级为悲观读锁stamp = sl.readLock();try {// 读入方法局部变量.....} finally {// 释放悲观读锁sl.unlockRead(stamp);}
}
// 使用方法局部变量执行业务操作
......
StampedLock 写模板:
long stamp = sl.writeLock();
try {// 写共享变量......
} finally {sl.unlockWrite(stamp);
}