StampedLock:一个并发编程中非常重要的票据锁( 二 )

在上述代码中 , 如果在执行乐观读操作时 , 另外的线程对共享变量进行了写操作 , 则会把乐观读升级为悲观读锁 , 如下代码片段所示 。
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);}这种将乐观读升级为悲观读锁的方式相比一直使用乐观读的方式更加合理 , 如果不升级为悲观读锁 , 则程序会在一个循环中反复执行乐观读操作 , 直到乐观读操作期间没有线程执行写操作 , 而在循环中不断的执行乐观读会消耗大量的CPU资源 , 升级为悲观读锁是更加合理的一种方式 。
StampedLock实现思想StampedLock内部是基于CLH锁实现的 , CLH是一种自旋锁 , 能够保证没有“饥饿现象”的发生 , 并且能够保证FIFO(先进先出)的服务顺序 。
在CLH中 , 锁维护一个等待线程队列 , 所有申请锁 , 但是没有成功的线程都会存入这个队列中 , 每一个节点代表一个线程 , 保存一个标记位(locked) , 用于判断当前线程是否已经释放锁 , 当locked标记位为true时 ,  表示获取到锁 , 当locked标记位为false时 , 表示成功释放了锁 。
当一个线程试图获得锁时 , 取得等待队列的尾部节点作为其前序节点 , 并使用类似如下代码判断前序节点是否已经成功释放锁:
while (pred.locked) { //省略操作}只要前序节点(pred)没有释放锁 , 则表示当前线程还不能继续执行 , 因此会自旋等待;反之 , 如果前序线程已经释放锁 , 则当前线程可以继续执行 。
释放锁时 , 也遵循这个逻辑 , 线程会将自身节点的locked位置标记为false , 后续等待的线程就能继续执行了 , 也就是已经释放了锁 。
StampedLock的实现思想总体来说 , 还是比较简单的 , 这里就不展开讲了 。
StampedLock的注意事项在读多写少的高并发环境下 , StampedLock的性能确实不错 , 但是它不能够完全取代ReadWriteLock 。在使用的时候 , 也需要特别注意以下几个方面 。
StampedLock不支持重入没错 , StampedLock是不支持重入的 , 也就是说 , 在使用StampedLock时 , 不能嵌套使用 , 这点在使用时要特别注意 。
StampedLock不支持条件变量第二个需要注意的是就是StampedLock不支持条件变量 , 无论是读锁还是写锁 , 都不支持条件变量 。
StampedLock使用不当会导致CPU飙升这点也是最重要的一点 , 在使用时需要特别注意:如果某个线程阻塞在StampedLock的readLock()或者writeLock()方法上时 , 此时调用阻塞线程的interrupt()方法中断线程 , 会导致CPU飙升到100% 。例如 , 下面的代码所示 。
public void testStampedLock() throws Exception{ final StampedLock lock = new StampedLock(); Thread thread01 = new Thread(()->{ // 获取写锁 lock.writeLock(); // 永远阻塞在此处 , 不释放写锁 LockSupport.park(); });thread01.start(); // 保证thread01获取写锁 Thread.sleep(100); Thread thread02 = new Thread(()-> //阻塞在悲观读锁 lock.readLock() );thread02.start(); // 保证T2阻塞在读锁 Thread.sleep(100); //中断线程thread02 //会导致线程thread02所在CPU飙升thread02.interrupt();thread02.join();}

经验总结扩展阅读