JDK8新增的StampedLock锁

StampedLock锁模式

  1. writeLock 写锁,独占锁或者排他锁、是阻塞的。

同时只有一个线程可以获取该锁,当一个线程获取该锁后,其它请求的线程必须等待,当目前没有线程持有读锁或者写锁的时候才可以获取到该锁,请求该锁成功后会返回一个stamp变量用来表示该锁的版本,当释放该锁时候需要unlockWrite并传递参数stamp

//返回一个可用于解锁或转换模式的标志
public long writeLock() {
            long s, next;  // 当完全解锁时才能获得下一个锁
            return ((((s = state) & ABITS) == 0L &&
                     U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
                                        next : acquireWrite(false, 0L));
}

使用情形:读少写多

  1. readLock 悲观读锁,共享锁,是非阻塞的。

没有线程获取独占写锁的情况下,同时多个线程可以获取该锁,如果已经有线程持有写锁,其他线程请求获取该读锁会被阻塞

//返回一个可用于解锁或转换模式的标志
public long readLock() {
    long s = state, next;  // 常见的非竞争情况上获取锁
    return ((whead == wtail && (s & ABITS) < RFULL &&
             U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
            next : acquireRead(false, 0L));
}

使用情形:读少写多

  1. tryOptimisticRead 乐观锁

是相对于悲观锁来说的,在操作数据前并没有通过CAS设置锁的状态,如果当前没有线程持有写锁,则简单的返回一个非0的stamp版本信息,获取该stamp后在具体操作数据前还需要调用validate验证下该stamp是否已经不可用,也就是看当调用tryOptimisticRead返回stamp后到到当前时间间是否有其他线程持有了写锁,如果是那么validate会返回0,否者就可以使用该stamp版本的锁对数据进行操作。由于tryOptimisticRead并没有使用CAS设置锁状态所以不需要显示的释放该锁

//Returns a stamp that can later be validated, or zero if exclusively locked.
public long tryOptimisticRead() {
    long s;
    return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}

使用情形:读多写少

TIPS:获取读锁只是使用与或操作进行检验,不涉及CAS操作,所以效率会高很多,但是同时由于没有使用真正的锁,在保证数据一致性上需要拷贝一份要操作的变量到方法栈,并且在操作数据时候可能其他写线程已经修改了数据,而我们操作的是方法栈里面的数据,也就是一个快照,所以最多返回的不是最新的数据,但是一致性还是得到保障的

JDK注释使用方式

class Point {
    private double x, y;
    private final StampedLock sl = new StampedLock();
    //独占锁
    void move(double deltaX, double deltaY) { // an exclusively locked method
     long stamp = sl.writeLock();
      try {
        x += deltaX;
        y += deltaY;
      } finally {
        sl.unlockWrite(stamp);
     }
   }
    //乐观锁
    double distanceFromOrigin() { // A read-only method
      //1. 获取乐观锁
      long stamp = sl.tryOptimisticRead();
      //2. 将全部变量拷贝到方法体栈内
      double currentX = x, currentY = y;
      //3. 校验锁是否被抢占
      if (!sl.validate(stamp)) {
         //4. 如果被抢占则获取一个共享读锁
         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);
          // 升级成功,则更新stamp,并设置坐标值,然后退出循环
          if (ws != 0L) {
            stamp = ws;
            x = newX;
            y = newY;
            break;
          }
          else {
            // 读锁升级写锁失败则释放度锁,显示获取独占锁,然后循环重试
            sl.unlockRead(stamp);
            stamp = sl.writeLock();
          }
        }
      } finally {
        sl.unlock(stamp);
      }
    }
  }}