StampedLock源码详解解析

19 篇文章 0 订阅

StampedLock

StampedLock 是Java.util.concurrent并发包里JDK1.8版本新增的一个锁,该锁是对读写锁ReentrantReadWriteLock的增强,优化了读锁、写锁的访问,同时使写锁之间可以相互转换,更细粒度控制并发。

StampedLock的主要特点概括一下,有以下几点:

  1. 所有获取锁的方法,都返回一个邮戳(Stamp),Stamp为0表示获取失败,其余都表示成功;
  2. 所有释放锁的方法,都需要一个邮戳(Stamp),这个Stamp必须是和成功获取锁时得到的Stamp一致;
  3. StampedLock是不可重入的;(如果一个线程已经持有了写锁,再去获取写锁的话就会造成死锁)
  4. StampedLock有三种访问模式:
    ①Reading(读模式):功能和ReentrantReadWriteLock的读锁类似
    ②Writing(写模式):功能和ReentrantReadWriteLock的写锁类似
    ③Optimistic reading(乐观读模式):这是一种优化的读模式。
  5. StampedLock支持读锁和写锁的相互转换
    我们知道RRW中,当线程获取到写锁后,可以降级为读锁,但是读锁是不能直接升级为写锁的。
    StampedLock提供了读锁和写锁相互转换的功能,使得该类支持更多的应用场景。
  6. 无论写锁还是读锁,都不支持Conditon等待
    在这里插入图片描述

StampedLock基本包含了ReentrantLock和ReentrantReadWriteLock的所有方法,是对ReentrantReadWriteLock锁的增强方法。他的出现是要解决ReentrantReadWriteLock在读写分离时的线程饥饿问题。当ReentrantReadWriteLock对其写锁想要获取的话,就必须没有任何其他读写锁存在才可以,这实现了悲观读取。如果读操作很多,写很少的情况下,线程有可能就会遭遇饥饿问题,而StampedLock正是解决了这个问题,优化了读锁和写锁之间的相互转换。

StampedLock的三种模式

  • 1、写入(Writing):writeLock是一个独占锁,也是一个悲观锁。
  • 2、读取(Reading):readLock这时候是一个悲观锁。
  • 3、乐观读取(Optimistic Reading):提供了tryOptimisticRead方法返回一个非0的stamp,只有当前同步状态没有被写模式所占有是才能获取到。他是在获取stamp值后对数据进行读取操作,最后验证该stamp值是否发生变化,如果发生变化则读取无效,代表有数据写入。这种方式能够降低竞争和提高吞吐量。

不同的是:StampedLock 里的写锁和悲观读锁加锁成功之后,都会返回一个 stamp;然后解锁的时候,需要传入这个 stamp。 try系列获取锁的函数,当获取锁失败后会返回为0的stamp值。当调用释放锁和转换锁的方法时候需要传入获取锁时候返回的stamp值。 StampedLock的内部实现是基于CLH锁的,CLH锁原理:锁维护着一个等待线程队列,所有申请锁且失败的线程都记录在队列。一个节点代表一个线程,保存着一个标记位locked,用以判断当前线程是否已经释放锁。当一个线程试图获取锁时,从队列尾节点作为前序节点,循环判断所有的前序节点是否已经成功释放锁。 它的核心思想在于,在乐观读的时候如果发生了写操作,应该通过重试的方式来获取新的值,而不应该阻塞写操作。这种模式也就是典型的无锁编程思想,和CAS自旋的思想一样。这种操作方式决定了StampedLock在读线程非常多而写线程非常少的场景下非常适用,同时还避免了写饥饿情况的发生。并且StampedLock不支持可重入,并且不支持Condition
在这里插入图片描述

属性
/**
 * @since 1.8
 * @author Doug Lea
 */
public class StampedLock implements java.io.Serializable {
	
	//处理器数量
	private static final int NCPU = Runtime.getRuntime().availableProcessors();
	//在当前节点加入队列前,如果队列头尾节点相等,即属性whead和wtail相等,
	//先让其自旋一定的大小,自旋的值 (1 << 6)=2^6=64
	private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
	//在阻塞当前线程前,如果队列头尾节点相等,即属性whead和wtail相等,
	//先让其自旋一定的大小,自旋的值(1 << 10)=2^10=1024
	private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
	//HEAD_SPINS的最大值(1 << 16)=2^16=65536
	private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
	//读锁大小溢出时,超过126,线程自增的随机数&上OVERFLOW_YIELD_RATE时会yeild,
	//下面具体实现中会解释道
	private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
	//读锁最大的bit位
	private static final int LG_READERS = 7;
	//获取悲观读成功时,state增加的值
	private static final long RUNIT = 1L;
	//写锁获取成功,state增加的值(LG_READERS=7),写锁标志位(1<<7)=2^7=128
	private static final long WBIT  = 1L << LG_READERS;
	//在获取当前读锁的个数,判断当前stampedLock是属于读锁的状态,127= 00...00 0011 1111
	private static final long RBITS = WBIT - 1L;
	//最大的读锁大小,126
	private static final long RFULL = RBITS - 1L;
	//包含读锁标志位和写锁标志位或起来,在获取锁和释放锁中使用,比如用于判断state是否处于
	//读锁还是写锁,还是无锁状态
	private static final long ABITS = RBITS | WBIT;
	//用于在乐观锁和释放锁使用,SBITS = 111...1100 0000
	private static final long SBITS = ~RBITS; // note overlap with ABITS
	//StampedLock初始化,state的初始值100000000=((1<<7)<<1)=(1<<8)=2^8=256
	private static final long ORIGIN = WBIT << 1;
	//如果当前线程被中断,获取读写锁时,返回的值
	private static final long INTERRUPTED = 1L;
	//节点的等待状态
	private static final int WAITING   = -1;
	//节点的取消状态
	private static final int CANCELLED =  1;
	//节点属于读模式
	private static final int RMODE = 0;
	//节点属于写模式
	private static final int WMODE = 1;
	//stampedLock队列中的头节点
	private transient volatile WNode whead;
	//stampedLock队列中的尾节点
	private transient volatile WNode wtail;
	//读锁的视图,不可重入,并且不支持condition
	transient ReadLockView readLockView;
	//写锁的视图,不可重入,并且不支持condition
	transient WriteLockView writeLockView;
	//读写锁的视图
	transient ReadWriteLockView readWriteLockView;
	//stampedLock的状态,用于判断当前stampedLock是属于读锁还是写锁还是乐观锁
	private transient volatile long state;
	//读锁溢出时,记录额外的读锁大小private transient int readerOverflow;
	//UnSafe的使用,如果不清楚的话,可以看我分享的美团的UnSafe介绍
	private static final sun.misc.Unsafe U;
	//属性state的偏移量,定义成静态的原因是所有的实例的内存布局是一样的,偏移量大小都是相等的
	private static final long STATE;
	//属性whead的偏移量
	private static final long WHEAD;
	//属性wtail的偏移量
	private static final long WTAIL;
	//内部类WNode的next属性偏移量
	private static final long WNEXT;
	//内部类WNode的status的属性偏移量
	private static final long WSTATUS;
	//内部类WNode的cowait属性偏移量,读模式的节点队列
	private static final long WCOWAIT;
	//Thread类中的parkBlocker属性偏移量,记录当前线程被那个对象阻塞起来
	private static final long PARKBLOCKER;
	static {    
	     try {        
	        U = sun.misc.Unsafe.getUnsafe();        
	        Class<?> k = StampedLock.class;        
	        Class<?> wk = WNode.class;        
	        STATE = U.objectFieldOffset(k.getDeclaredField("state"));        
	        WHEAD = U.objectFieldOffset(k.getDeclaredField("whead"));        
	        WTAIL = U.objectFieldOffset(k.getDeclaredField("wtail"));        
	        WSTATUS = U.objectFieldOffset(wk.getDeclaredField("status"));        
	        WNEXT = U.objectFieldOffset(wk.getDeclaredField("next"));        
	        WCOWAIT = U.objectFieldOffset(wk.getDeclaredField("cowait"));        
	        Class<?> tk = Thread.class;        
	        PARKBLOCKER = U.objectFieldOffset(tk.getDeclaredField("parkBlocker"));    
	} catch (Exception e) {        throw new Error(e);    }}

}

构造函数

//state被初始化为256=1<<8,写锁标志位128
public StampedLock() {    
	//注意state的类型是long
	state = ORIGIN;
}

StampedLock并没有什么参数需要设置,所以构造函数非常简单,但看起来莫名其妙,为什么state直接等于256。不过可以推测StampedLock也是类似于ReentrantReadWriteLock使用一个state的二进制位来标识锁的状态,这里为了方便理解代码,我直接说明它是如何使用二进制位的。
在这里插入图片描述
StampedLock用了long型作为state,这里是将其64位划分为3部分使用。低7位作为读锁的标志位,可以由多个线程共享,每有一个线程加了读锁,低7位就加1,那是不是只能有127个读者?当然不是,还有其他机制可以额外记录超过127的读者。第8位是写锁位,由线程独占。其余位是stamp位,记录有没有写锁状态的变化,每使用一次写锁,stamped位就会增加1,相当于整个state加了256。

了解了以上信息,我们就可以大概猜测到StampedLock的运行机制了,接下我们结合代码来验证下我们的猜测。

内部类

//等待节点类
static final class WNode {
    //前驱节点    
    volatile WNode prev;
    //当前节点的下一个节点    
    volatile WNode next;
    //等待的读模式节点    
    volatile WNode cowait;    // list of linked readers
    //节点对应的线程    
     volatile Thread thread;   // non-null while possibly parked
    //节点的状态    
    volatile int status;      // 0, WAITING, or CANCELLED
    //当前节点是处于读锁模式还是写锁模式    
    final int mode;           // RMODE or WMODE
    //构造函数,传入读写锁模式和前驱节点    
     WNode(int m, WNode p) { mode = m; prev = p; }
}
 
 
//读锁视图
final class ReadLockView implements Lock {    
    public void lock() {
         //调用StampedLock的readLock方法,下面会介绍 
         readLock(); 
    }    
    public void lockInterruptibly() throws InterruptedException {
        //调用StampedLock的readLockInterruptibly方法,下面会介绍        
         readLockInterruptibly();    
    }    
    public boolean tryLock() { 
         //调用StampedLock的tryReadLock方法,下面会介绍
         return tryReadLock() != 0L; 
    }    
    
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        //调用StampedLock的tryReadLock(time, unit)方法        
        return tryReadLock(time, unit) != 0L;    
    }    
    public void unlock() {
        //调用StampedLock的unstampedUnlockRead方法
        unstampedUnlockRead(); 
    }    
    //不支持Condition,和ReadWriteLock的区别
    public Condition newCondition() {        
        throw new UnsupportedOperationException();    
    }
}
   
 
//写锁视图
final class WriteLockView implements Lock {    
    public void lock() { 
       //调用StampedLock的writeLock方法,下面会详细介绍
       writeLock(); 
    }    
    public void lockInterruptibly() throws InterruptedException {
        //调用StampedLock的writeLockInterruptibly方法,下面会详细介绍        
        writeLockInterruptibly();    
   }    
   public boolean tryLock() {
        //调用StampedLock的tryWriteLock方法,下面会详细介绍
        return tryWriteLock() != 0L; 
   }    
   //调用StampedLock的tryWriteLock(time, unit)方法,下面会详细介绍
   public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return tryWriteLock(time, unit) != 0L;    
   }    
    public void unlock() { 
        //调用StampedLock的unstampedUnlockWrite方法        
        unstampedUnlockWrite(); 
    }    
    //不支持Condition
    public Condition newCondition() {        
        throw new UnsupportedOperationException();    
     }
}
 
//读写锁视图
final class ReadWriteLockView implements ReadWriteLock {    
    //获取读锁视图,如果读锁视图未初始化,初始化读锁视图
    public Lock readLock() { 
    	//return ((v = readLockView)!=null?v:(readLockView = new ReadLockView()));
        return asReadLock();
    }
    //获取写锁视图,如果写锁视图未初始化,初始化写锁视图    
    public Lock writeLock() { 
    	//((v = writeLockView) != null ?v:(writeLockView = new WriteLockView()));
       return asWriteLock(); 
    }
}

写锁的获取、释放

1.写锁的获取

/**
* 获取排他锁,如果不能马上获取到,必要的时候会将其阻塞,writeLock方法不支持中断操作
**/
public long writeLock() {    
    long s, next;  // bypass acquireWrite in fully unlocked case only    
    //如果当前StampedLock的状态state为初始状态即256(100000000),&上255等于0,表明当前属于
    //无锁状态,写锁可以获取成功,由于是对共享变量的操作,使用cas,进行变量的更新。
    //否则&255不等于0表明当前处于有锁状态,调用acquireWrite方法,下面会介绍  
    return ((((s = state) & ABITS) == 0L
    			&& U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ? next 
		    	: acquireWrite(false, 0L));
}
 
/**
* 非阻塞的获取写锁,如果获取写锁失败返回stamp为0,如果当前处于无锁状态并且cas更新StampedLock
* 的state属性成功,返回s+WBIT(1<<7)的stamp
**/
public long tryWriteLock() {
    long s, next;
    return ((((s = state) & ABITS) == 0L &&
             U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
            next : 0L);
}
 
//超时的获取写锁,并且支持中断操作
public long tryWriteLock(long time, TimeUnit unit)
    throws InterruptedException {
    //将其时间转成纳秒  
    long nanos = unit.toNanos(time);
    if (!Thread.interrupted()) {
        long next, deadline;
        //先调用上面介绍的非阻塞tryWriteLock获取写锁,如果能加锁成功直接返回
        if ((next = tryWriteLock()) != 0L)
            return next;
        //如果时间小于等于0直接返回,加写锁失败  
        if (nanos <= 0L)
            return 0L;
        //System.nanoTime()可能返回负,如果和传入的时间相加等于0,deadline等于1
        if ((deadline = System.nanoTime() + nanos) == 0L)
            deadline = 1L;
        //调用下面介绍的acquireWrite方法,如果超时,返回的结果不是中断的值INTERRUPTED,
        //加锁成功,返回对应的Stamp值(state+WBIT)
        if ((next = acquireWrite(true, deadline)) != INTERRUPTED)
            return next;
    }
    //否则抛出中断异常
    throw new InterruptedException();
}
 
//中断的获取写锁,获取不到写锁抛出中断异常
public long writeLockInterruptibly() throws InterruptedException {
        long next;
        //当前线程没有被中断,并且调用acquireWrite方法不是返回INTERRUPTED中断标志位,
        //否则抛出中断异常,如果返回的标志位是0,也表示获取写锁失败
        if (!Thread.interrupted() &&
            (next = acquireWrite(true, 0L)) != INTERRUPTED)
            return next;
        //抛出中断异常
        throw new InterruptedException();
}
 
/**
* 获取锁,如果失败加入队列
**/
private long acquireWrite(boolean interruptible, long deadline) {    
    WNode node = null, p;    
    //将其当前节点,作为队列的尾节点,如果当前队列头结点和尾节点相等,并且StampedLock的状态
    //属性state为写锁WBIT,先自旋一段时间,如果自旋还是没有获取写锁成功,
    //再将其作为队列的尾节点加入
    for (int spins = -1;;) { // 自旋       
          long m, s, ns;
          //如果当前state等于256,属于无锁状态,直接加写锁,如果加锁成功直接返回         
          if ((m = (s = state) & ABITS) == 0L) {            
               if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT)) 
                   return ns;        
          } 
          //加锁失败,如果spins小于0,并且当前的StampedLock属于写锁状态,以及头尾节点相等,
          //spins赋值SPINS(自旋的值 (1 << 6)=2^6=64),让其当前线程自旋一段时间获取写锁 
          else if (spins < 0)            
               spins = (m == WBIT && wtail == whead) ? SPINS : 0; 
          else if (spins > 0) {
               // LockSupport.nextSecondarySeed() >= 0永真             
               if (LockSupport.nextSecondarySeed() >= 0)                
                   --spins;        
          }
          //如果当前队列为空队列,即尾节点为空,初始化队列         
          else if ((p = wtail) == null) { // initialize queue
               //构造写模式的头节点             
               WNode hd = new WNode(WMODE, null);
               //如果当前头结点设置成功,将其尾节点设置为头结点            
               if (U.compareAndSwapObject(this, WHEAD, null, hd))  
                   wtail = hd;        
          }
          //如果当前节点为空,初始化当前节点,前驱节点为上一次的尾节点        
          else if (node == null)            
               node = new WNode(WMODE, p);
          //如果尾节点已经改变,重新设置当前节点的前驱节点         
          else if (node.prev != p)            
               node.prev = p;
          //将其当前节点设置为尾节点,如果写锁获取成功,直接退出        
          else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
               p.next = node;            
               break;        
          }    
	  }
	   
	  //阻塞当前线程,再阻塞当前线程之前,如果头节点和尾节点相等,让其自旋一段时间获取写锁。
	  //如果头结点不为空,释放头节点的cowait队列    
	  for (int spins = -1;;) {
            WNode h, np, pp; int ps;
            //头尾节点相等
            if ((h = whead) == p) {
                //设置初始自旋的值:HEAD_SPINS=(1 << 10)=2^10=1024
                if (spins < 0)
                    spins = HEAD_SPINS;
                //如果spins还是小于MAX_HEAD_SPINS,将其扩大2倍
                else if (spins < MAX_HEAD_SPINS)
                    spins <<= 1;
                //自旋获取写锁
                for (int k = spins;;) { // spin at head
                    long s, ns;
                    //如果写锁设置成功,将其当前节点的前驱节点设置为空,
                    //并且将其节点设置为头节点 
                    if (((s = state) & ABITS) == 0L) {
                        if (U.compareAndSwapLong(this, STATE, s,
                                                 ns = s + WBIT)) {
                            whead = node;
                            node.prev = null;
                            return ns;
                        }
                    }
                    //LockSupport.nextSecondarySeed() >= 0永真,k做自减操作    
                    else if (LockSupport.nextSecondarySeed() >= 0 &&
                             --k <= 0)
                        break;
                }
            }
            //如果头结点不为空
            else if (h != null) {
                WNode c; Thread w;
                //如果头结点的cowait队列(RMODE的节点)不为空,唤醒cowait队列
                while ((c = h.cowait) != null) {
                    //cowait节点和对应的节点都不为空唤醒其线程,
                    //循环的唤醒cowait节点队列中Thread不为空的线程
                    if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                        (w = c.thread) != null)
                        U.unpark(w);
                }
            }
            //如果头结点不变
            if (whead == h) {
                //如果当前节点的前驱节点和尾节点不一致,将p设置为当前节点的前驱节点,
                //可以使节点往头节点移动
                if ((np = node.prev) != p) {
                    if (np != null)
                        //将其p设置为当前节点的前驱节点
                        (p = np).next = node;
                }
                //如果当前节点的前驱节点状态为0,将其前驱节点设置为等待状态
                else if ((ps = p.status) == 0)
                    U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
                //如果当前节点的前驱节点状态为取消 
                else if (ps == CANCELLED) {
                    //重新设置当前节点的前驱节点
                    if ((pp = p.prev) != null) {
                        node.prev = pp;
                        pp.next = node;
                    }
                }
                else {
                    long time; 
                    //如果传入的时间为0,阻塞直到UnSafe.unpark唤醒
                    if (deadline == 0L)
                        time = 0L;
                    //如果时间已经超时,取消当前的等待节点
                    else if ((time = deadline - System.nanoTime()) <= 0L)
                        //取消节点的cancelWaiter方法下面会介绍
                        return cancelWaiter(node, node, false);
                    //获取当前线程
                    Thread wt = Thread.currentThread();
                    //设置线程Thread的parkblocker属性,表示当前线程被谁阻塞,用于
                    //监控线程使用
                    U.putObject(wt, PARKBLOCKER, this);
                    //将其当前线程设置为当前节点
                    node.thread = wt;
                    //当前节点的前驱节点为等待状态,并且队列的头节点和尾节点不相等或者
                    //StampedLock当前状态为有锁状态,队列头节点不变,当前节点的前驱
                    //节点不变,阻塞当前线程
                    if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
                        whead == h && node.prev == p)
                        //模拟阻塞当前线程,只有调用UnSafe.unpark()唤醒,如果time不等于0,
                        //时间到也会自动唤醒
                        U.park(false, time);
                    //当前节点的线程置为空  
                    node.thread = null;
                    //当前线程的监控对象也置为空
                    U.putObject(wt, PARKBLOCKER, null);
                    //如果传入的参数interruptible为true,并且当前线程中断,取消当前节点
                    if (interruptible && Thread.interrupted())
                        //取消节点的cancelWaiter方法下面会介绍
                        return cancelWaiter(node, node, true);
                }
            }
        }
 
 
//取消等待节点
private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
       //node和group为同一节点,要取消的节点,都不为空时  
       if (node != null && group != null) {
            Thread w;
            //将其当前节点的状态设置为取消状态
            node.status = CANCELLED;
            //如果当前要取消节点的cowait队列不为空,将其cowait队列中取消的节点去除
            for (WNode p = group, q; (q = p.cowait) != null;) {
                if (q.status == CANCELLED) {
                    U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
                    p = group; 
                }
                else
                    p = q;
            }
            
            //group和node为同一节点
            if (group == node) {
                //唤醒状态没有取消的cowait队列中的节点
                for (WNode r = group.cowait; r != null; r = r.cowait) {
                    if ((w = r.thread) != null)
                        U.unpark(w); 
                }
                //将其当前取消节点的前驱节点的下一个节点设置为当前取消节点的next节点
                for (WNode pred = node.prev; pred != null; ) { // unsplice
                    WNode succ, pp;
                    //如果当前取消节点的下一个节点为空或者是取消状态,
                    //从尾节点开始,寻找有效的节点 
                    while ((succ = node.next) == null ||
                           succ.status == CANCELLED) {
                        WNode q = null;    // find successor the slow way
                        //从尾节点开始寻找当前取消节点的下一个节点
                        for (WNode t = wtail; t != null && t != node; t = t.prev)
                            if (t.status != CANCELLED)
                                q = t;     
                        //如果当前取消节点的next节点和从尾节点寻找的节点相等,
                        //或者将其寻找的节点q设置为下一个节点成功
                        if (succ == q ||   
                            U.compareAndSwapObject(node, WNEXT,
                                                   succ, succ = q)) {
                            //判断当前取消节点的曾经的下一个节点为空并且当前取消节点为尾节点
                            if (succ == null && node == wtail)
                                //将其尾节点设置为当前取消节点的前驱节点
                                U.compareAndSwapObject(this, WTAIL, node, pred);
                            break;
                        }
                    }
                    //如果当前取消节点的前驱节点的下一节点为当前取消节点
                    if (pred.next == node) // unsplice pred link
                        //将其前驱节点的下一节点设置为当前取消节点的next有效节点
                        U.compareAndSwapObject(pred, WNEXT, node, succ);
                    //唤醒当前取消节点的下一节点,观察其新的前驱节点 
                    if (succ != null && (w = succ.thread) != null) {
                        succ.thread = null;
                        U.unpark(w);       
                    }
                    //如果当前取消节点的前驱节点状态不是取消状态,或者其前驱节点的前驱节点
                    //为空,直接退出循环 
                    if (pred.status != CANCELLED || (pp = pred.prev) == null)
                        break;
                    //重新设置当前取消节点的前驱节点
                    node.prev = pp;
                    //重新设置pp的下一节点  
                    U.compareAndSwapObject(pp, WNEXT, pred, succ);
                    //将其前驱节点设置为pp,重新循环
                    pred = pp;
                }
            }
        }
        WNode h; // Possibly release first waiter
        //头节点不为空
        while ((h = whead) != null) {
            long s; WNode q; // similar to release() but check eligibility
            //头节点的下一节点为空或者是取消状态,从尾节点开始寻找有效的节点(包括等待状态,
            //和运行状态)
            if ((q = h.next) == null || q.status == CANCELLED) {
                for (WNode t = wtail; t != null && t != h; t = t.prev)
                    if (t.status <= 0)
                        q = t;
            }
            //如果头节点没有改变
            if (h == whead) {
                //头节点的下一有效节点不为空,并且头节点的状态为0,并且当前StampedLock的
                //不为写锁状态,并且头节点的下一节点为读模式,唤醒头结点的下一节点
                if (q != null && h.status == 0 &&
                    ((s = state) & ABITS) != WBIT && // waiter is eligible
                    (s == 0L || q.mode == RMODE))
                    //唤醒头结点的下一有效节点,下面会介绍release方法
                    release(h);
                break;
            }
        }
        //如果当前线程被中断或者传入进来的interrupted为true,直接返回中断标志位,否则返回0 
        return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
    }
}
 
 
//唤醒头结点的下一有效节点
private void release(WNode h) {
        //头结点不为空
        if (h != null) {
            WNode q; Thread w;
            //如果头结点的状态为等待状态,将其状态设置为0
            U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
            //从尾节点开始,到头节点结束,寻找状态为等待或者0的有效节点
            if ((q = h.next) == null || q.status == CANCELLED) {
                for (WNode t = wtail; t != null && t != h; t = t.prev)
                    if (t.status <= 0)
                        q = t;
            }
            //如果寻找到有效节点不为空,并且其对应的线程也不为空,唤醒其线程
            if (q != null && (w = q.thread) != null)
                U.unpark(w);
        }
}

2.写锁的释放

//根据传入的stamp释放写锁
public void unlockWrite(long stamp) {
        WNode h;
        //如果当前StampedLock的锁状态state和传入进来的stamp不匹配或者传入进来的
        //不是写锁标志位,往外抛出IllegalMonitorStateException异常
        if (state != stamp || (stamp & WBIT) == 0L)
            throw new IllegalMonitorStateException();
        /** * 释放写锁为什么不直接减去stamp,再加上ORIGIN,而是
        (stamp += WBIT) == 0L ? ORIGIN : stamp来释放写锁,
        位操作表示如下:  stamp += WBIT 即
        0010 0000 0000 = 0001 1000 0000 + 0000 1000 0000 这一步操作是重点!!!
        写锁的释放并不是像ReentrantReadWriteLock一样+1然后-1,而是通过再次加0000 1000 0000
        来使高位每次都产生变化,为什么要这样做?直接减掉0000 1000 0000不就可以了吗?
        这就是为了后面乐观锁做铺垫,让每次写锁都留下痕迹。 大家知道cas ABA的问题,
        字母A变化为B能看到变化,如果在一段时间内从A变到B然后又变到A,在内存中自会显示A,
        而不能记录变化的过程。在StampedLock中就是通过每次对高位加0000 1000 0000来达到记录写锁
        操作的过程,可以通过下面的步骤理解: 
        第一次获取写锁: 0001 0000 0000 + 0000 1000 0000 = 0001 1000 0000 
        第一次释放写锁: 0001 1000 0000 + 0000 1000 0000 = 0010 0000 0000 
        第二次获取写锁: 0010 0000 0000 + 0000 1000 0000 = 0010 1000 0000 
        第二次释放写锁: 0010 1000 0000 + 0000 1000 0000 = 0011 0000 0000 
        第n次获取写锁:  1110 0000 0000 + 0000 1000 0000 = 1110 1000 0000
        第n次释放写锁:  1110 1000 0000 + 0000 1000 0000 = 1111 0000 0000 
        可以看到第8位在获取和释放写锁时会产生变化,也就是说第8位是用来表示写锁状态的,
        前7位是用来表示读锁状态的,8位之后是用来表示写锁的获取次数的。这样就有效的解决了ABA问题
        ,留下了每次写锁的记录,也为后面乐观锁检查变化提供了基础。 */
        state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
        //头结点不为空,并且头结点的状态不为0
        if ((h = whead) != null && h.status != 0)
            //看上面release方法的介绍 
            release(h);
}

//无需传入stamp释放写锁
public boolean tryUnlockWrite() {
        long s; WNode h;
        //如果当前StampedLock的锁状态state不是写锁状态,直接返回释放失败
        if (((s = state) & WBIT) != 0L) {
            //看上面解释 释放写锁为什么不直接减去stamp,再加上ORIGIN           
            state = (s += WBIT) == 0L ? ORIGIN : s;
            //头结点不为空,并且头结点的状态不为0           
            if ((h = whead) != null && h.status != 0)
                //看上面release方法的介绍
                release(h);
            return true;
        }
        return false;
}

/**读写锁都可以释放,如果锁状态匹配给定的邮票,释放锁的相应模式,StampedLock的state处于乐观读时,
* 不能调用此方法,因为乐观读不是锁
**/
public void unlock(long stamp) {
        long a = stamp & ABITS, m, s; WNode h;
        //
        while (((s = state) & SBITS) == (stamp & SBITS)) {
            //如果当前处于无锁状态,或者乐观读状态,直接退出,抛出异常
            if ((m = s & ABITS) == 0L)
                break;
            //如果当前StampedLock的状态为写模式
            else if (m == WBIT) {
                //传入进来的stamp不是写模式,直接退出,抛出异常
                if (a != m)
                    break;
                //否则的话释放写锁,看上面解释为什么不是直接减去WBIT,再加上ORIGIN
                state = (s += WBIT) == 0L ? ORIGIN : s;
                //头结点不为空,并且头结点的状态不为0   
                if ((h = whead) != null && h.status != 0)
                    //看看上面release方法的介绍 
                    release(h);
                return;
            }
            //如果传入进来的状态是无锁模式,或者是乐观读模式,直接退出,抛出异常
            else if (a == 0L || a >= WBIT)
                break;
            //如果处于读锁模式,并且读锁没有溢出
            else if (m < RFULL) {
                //cas操作使StampedLock的state状态减1,释放一个读锁,失败时,重新循环
                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                    //如果当前读锁只有一个,并且头结点不为空,并且头结点的状态不为0
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        //看上面的release方法介绍
                        release(h);
                    return;
                }
            }
            //tryDecReaderOverflow方法看下面介绍
            else if (tryDecReaderOverflow(s) != 0L)
                return;
        }
        //抛出异常
        throw new IllegalMonitorStateException();
}

private long tryDecReaderOverflow(long s) {
        //如果当前StampedLock的state的读模式已满,s&ABITS为126
        if ((s & ABITS) == RFULL) {
            //先将其state设置为127
            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
                int r; long next;
                //如果当前readerOverflow(记录溢出的读锁个数)大于0
                if ((r = readerOverflow) > 0) {
                    //readerOverflow做减1操作
                    readerOverflow = r - 1;
                    //将其next设置为原来的state
                    next = s;
                }
                else
                    //否则的话,将其当前的state做减1操作 
                    next = s - RUNIT;
                 //将其state设置为next
                 state = next;
                 return next;
            }
        }
        //如果当前线程随机数&上7要是等于0,线程让步
        else if ((LockSupport.nextSecondarySeed() &
                  OVERFLOW_YIELD_RATE) == 0)
            Thread.yield();
        return 0L;
}


读锁的获取和释放

1.读锁的获取

//获取非排它性锁,读锁,如果获取不到读锁,阻塞直到可用,并且该方法不支持中断操作
public long readLock() {
        long s = state, next;
        //如果头结点和尾节点相等(因为如果StampedLock只有非排他性锁,读锁或者乐观读,
        //队列中只存在一个相同的节点),并且目前的读锁个数小于126,然后cas进行state的加1操作,
        //如果获取成功直接退出,否则执行acquireRead方法
        return ((whead == wtail && (s & ABITS) < RFULL &&
                 U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
                next : acquireRead(false, 0L));//acquireRead方法会在下面进行介绍
}

//非阻塞的获取非排他性锁,读锁,如果获取成功直接返回stamp的long值,否则返回0
public long tryReadLock() {
        for (;;) {
            long s, m, next;
            //如果目前StampedLock的状态为写锁状态,直接返回0,获取读锁失败
            if ((m = (s = state) & ABITS) == WBIT)
                return 0L;
            //如果当前状态处于读锁状态,并且读锁没有溢出 
            else if (m < RFULL) {
                //使用cas操作使state进行加1操作,如果cas成功,直接返回next,否则重新进行循环
                if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                    return next;
            }
            //如果读锁溢出,调用下面介绍的tryIncReaderOverflow方法,如果操作成功,直接返回,
            //否则重新进行循环操作
            else if ((next = tryIncReaderOverflow(s)) != 0L)
                return next;
        }
}

//记录溢出读锁的个数
private long tryIncReaderOverflow(long s) {
        //如果state&上255等于126 
        if ((s & ABITS) == RFULL) {
            // 使用cas操作将其state设置为127
            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
                //将其记录额外溢出的读锁个数进行加1操作
                ++readerOverflow;
                //将其state重新置为原来的值
                state = s;
                //返回传进来的值
                return s;
            }
        }
        //如果当前线程的随机数增加操作&上7等于0,将其线程进行让步操作
        else if ((LockSupport.nextSecondarySeed() &
                  OVERFLOW_YIELD_RATE) == 0)
            Thread.yield();
        //否则直接返回0失败
        return 0L;
}

//超时的获取非排他性锁,读锁,并且支持中断操作
public long tryReadLock(long time, TimeUnit unit)
        throws InterruptedException {
        long s, m, next, deadline;
        long nanos = unit.toNanos(time);
        //如果当前线程没有被中断
        if (!Thread.interrupted()) {
            //并且当前StampedLock的状态不处于写锁状态 
            if ((m = (s = state) & ABITS) != WBIT) {
                //并且读锁没有溢出,使用cas操作state加1,如果成功直接返回
                if (m < RFULL) {
                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                        return next;
                }
                //如果读锁溢出,调用tryIncReaderOverflow方法,看上面的介绍 
                else if ((next = tryIncReaderOverflow(s)) != 0L)
                    return next;
            }
            //如果超时时间小于等于0,直接返回0,获取读锁失败
            if (nanos <= 0L)
                return 0L;
            //如果System.nanoTime加上nanos等于0,将其deadline时间设置为1,
            //因为System.nanoTime可能为负数
            if ((deadline = System.nanoTime() + nanos) == 0L)
                deadline = 1L;
            //如果调用acquireRead方法返回不是中断的标志位INTERRUPTED,直接返回,
            //next不等于0获取读锁成功,否则获取读锁失败
            if ((next = acquireRead(true, deadline)) != INTERRUPTED)
                return next;
        }
   //抛出中断异常            throw new InterruptedException();
}

//获取非排它性锁,读锁,如果获取不到读锁,阻塞直到可用,并且该方法支持中断操作       
public long readLockInterruptibly() throws InterruptedException {
        long next;
        //如果当前线程没有被中断,并且调用acquireRead方法没有返回被中断的标志位INTERRUPTED
        //回来,直接退出,next值不等于0获取读锁成功
        if (!Thread.interrupted() &&
            (next = acquireRead(true, 0L)) != INTERRUPTED)
            return next;
        //抛出被中断异常
        throw new InterruptedException();
}    

/** 支持中断和超时的获取读锁,这个方法主要做的事情包括,如果头结点和尾节点相等,
自旋一段时间,获取读锁,否则的话,如果队列为空,构建头尾节点,如果当前队列头节点
和尾节点相等或者是当前StampedLock处于写锁状态,初始化当前节点,将其设置成尾节点。
如果头结点的cwait队列不为空,唤醒cwait队列的线程,将其当前节点阻塞,直到被唤醒可用
**/
private long acquireRead(boolean interruptible, long deadline) {
        WNode node = null, p;
        /** 如果头结点和尾节点相等,先让其线程自旋一段时间,如果队列为空初始化队列,
        生成头结点和尾节点。如果自旋操作没有获取到锁,并且头结点和尾节点相等,或者当前
        stampedLock的状态为写锁状态,将其当前节点加入队列中,如果加入当前队列失败,
        或者头结点和尾节点不相等,或者当前处于读锁状态,将其加入尾节点的cwait中,
        如果头结点的cwait节点不为空,并且线程也不为空,唤醒其cwait队列,阻塞当前节点**/
        for (int spins = -1;;) {
            WNode h;
            //如果头尾节点相等,先让其自旋一段时间
            if ((h = whead) == (p = wtail)) {
                for (long m, s, ns;;) {
                    //如果当前StampedLock的state状态为读锁状态,并且读锁没有溢出,
                    //使用cas操作state进行加1操作 
                    if ((m = (s = state) & ABITS) < RFULL ?
                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                        //否则当前处于读锁,并且读锁溢出,调用tryIncReaderOverflow方法,
                        //看上面的此方法的介绍
                        (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
                        return ns;
                    //如果当前状态处于写锁状态,或者大于写锁的状态
                    else if (m >= WBIT) {
                        //spins大于0,让其做自减操作
                        if (spins > 0) {
                            if (LockSupport.nextSecondarySeed() >= 0)
                                --spins;
                        }
                        else {
                            //如果自旋操作spins减到0
                            if (spins == 0) {
                                WNode nh = whead, np = wtail;
                                //如果头尾结点没有改变,或者新的头尾节点不相等,退出自旋 
                                if ((nh == h && np == p) || (h = nh) != (p = np))
                                	break;
                            }
                            //赋予自旋的初始值
                            spins = SPINS;
                        }
                    }
                }
            }
            //如果尾节点为空,初始化队列
            if (p == null) { // initialize queue
                //构造头结点
                WNode hd = new WNode(WMODE, null);
                //使用cas构造队列的头结点,如果成功,将其尾节点设置为头结点
                if (U.compareAndSwapObject(this, WHEAD, null, hd))
                    wtail = hd;
            }
            //如果当前节点为空,构造当前节点
            else if (node == null)
                node = new WNode(RMODE, p);
            //如果头结点和尾节点相等,或者当前StampedLock的state状态不为读锁状态
            else if (h == p || p.mode != RMODE) {
                //如果当前节点的前驱节点不是尾节点,重新设置当前节点的前驱节点
                if (node.prev != p)
                    node.prev = p;
                //将其当前节点加入队列中,并且当前节点做为尾节点,如果成功,直接退出循环操作
                else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
                    p.next = node;
                    break;
                }
            }
            //将其当前节点加入尾节点的cowait队列中,如果失败,将其当前节点的cowait置为null
            else if (!U.compareAndSwapObject(p, WCOWAIT,
                                             node.cowait = p.cowait, node))
                node.cowait = null;
            //如果当前队列不为空,当前节点不为空,并且头结点和尾节点不相等,并且当前
            //StampedLock的状态为读锁状态,并且当前节点cas加入尾节点的cowait队列中失败
            else {
                for (;;) {
                    WNode pp, c; Thread w;
                    //如果头结点的cowait队列不为空,并且其线程也不为null,
                    //将其cowait队列唤醒
                    if ((h = whead) != null && (c = h.cowait) != null &&
                        U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                        (w = c.thread) != null) // help release
                        //唤醒cowait队列中的节点线程
                        U.unpark(w);
                    //如果当前头结点为尾节点的前驱节点,或者头尾节点相等,
                    //或者尾节点的前驱节点为空
                    if (h == (pp = p.prev) || h == p || pp == null) {
                        long m, s, ns;
                        do {
                            //判断当前状态是否处于读锁状态,如果是,并且读锁没有溢出,
                            //state进行cas加1操作
                            if ((m = (s = state) & ABITS) < RFULL ?
                                U.compareAndSwapLong(this, STATE, s,
                                                     ns = s + RUNIT) :
                                (m < WBIT &&
                                //否则进行溢出操作,看上面tryIncReaderOverflow方法介绍
                                 (ns = tryIncReaderOverflow(s)) != 0L))
                                return ns;
                        //当前StampedLock的state状态不是写模式,才能进行循环操作
                        } while (m < WBIT);
                    }
                    //如果头结点没有改变,并且尾节点的前驱节点不变
                    if (whead == h && p.prev == pp) {
                        long time;
                        //如果尾节点的前驱节点为空,或者头尾节点相等,或者尾节点的状态为取消
                        if (pp == null || h == p || p.status > 0) {
                            //将其当前节点设置为空,退出循环
                            node = null; // throw away
                            break;
                        }
                        //如果超时时间为0,会一直阻塞,直到调用UnSafe的unpark方法
                        if (deadline == 0L)
                            time = 0L;
                        //如果传入的超时时间已经过期,将当前节点取消,
                        ///看上面cancelWaiter方法的介绍
                        else if ((time = deadline - System.nanoTime()) <= 0L)
                            return cancelWaiter(node, p, false);
                        //获取当前线程 
                        Thread wt = Thread.currentThread();
                        //设置当前线程被谁阻塞的监控对象
                        U.putObject(wt, PARKBLOCKER, this);
                        //将其当前节点的线程设置为当前线程
                        node.thread = wt;
                        //如果头节点和尾节点的前驱节点不相等,或者当前StampedLock的
                        //state状态为写锁,并且头结点不变,尾节点的前驱节点不变
                        if ((h != pp || (state & ABITS) == WBIT) &&
                            whead == h && p.prev == pp)
                            //调用UnSafe的park来进行阻塞当前线程 
                            U.park(false, time);
                        //将其当前节点的线程置为空
                        node.thread = null;
                        //将其当前线程的监控对象置为空
                        U.putObject(wt, PARKBLOCKER, null);
                        //如果传入进来的interruptible是要求中断的,并且当前线程被中断
                        if (interruptible && Thread.interrupted())
                            //看上面cancelWaiter方法的介绍 
                            return cancelWaiter(node, p, true);
                    }
                }
            }
        }
        //阻塞当前线程,再阻塞当前线程之前,如果头节点和尾节点相等,让其自旋一段时间获取写锁。
        //如果头结点不为空,释放头节点的cowait队列    
        for (int spins = -1;;) {
            WNode h, np, pp; int ps;
            //如果头节点和尾节点相等
            if ((h = whead) == p) {
                //自旋的初始值 
                if (spins < 0)
                    spins = HEAD_SPINS;
                //如果spins小于MAX_HEAD_SPINS
                else if (spins < MAX_HEAD_SPINS)
                    //将其spins进行扩大两倍
                    spins <<= 1;
                //自旋一段时间获取读锁
                for (int k = spins;;) { // spin at head
                    long m, s, ns;
                    //如果当前状态为无锁或者读锁模式 
                    if ((m = (s = state) & ABITS) < RFULL ?
                        //state进行cas加1操作
                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                        //如果当前state状态为读锁状态,并且读锁溢出,
                        //使用tryIncReaderOverflows方法进行溢出的读锁数累加
                        (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
                        WNode c; Thread w;
                        //将其节点设置为头结点
                        whead = node;
                        //将其当前节点的前驱节点设置为空
                        node.prev = null;
                        //如果当前节点的cowait队列不为空,循环的唤醒cowait队列中,
                        //线程不为空的线程
                        while ((c = node.cowait) != null) {
                            if (U.compareAndSwapObject(node, WCOWAIT,
                                                       c, c.cowait) &&
                                (w = c.thread) != null)
                                U.unpark(w);
                        }
                        return ns;
                    }
                    //如果当前状态为写状态,采取自减操作
                    else if (m >= WBIT &&
                             LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
                        break;
                }
            }
            //如果头结点不为空 
            else if (h != null) {
                WNode c; Thread w;
                //头结点的cowait队列不为空,循环的唤醒的cowait队列中,线程不为空的节点的线程
                while ((c = h.cowait) != null) {
                    if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                        (w = c.thread) != null)
                        //使用UnSafe进行唤醒
                        U.unpark(w);
                }
            }
            //如果头结点没改变
            if (whead == h) {
                //如果当前节点的前驱节点不等于尾节点
                if ((np = node.prev) != p) {
                    //当前节点的前驱节点不为空 
                    if (np != null)
                        //将其p设置为当前节点的前驱节点,如果前面的节点已经被唤醒,将p设置
                        //为当前节点的前驱节点,有可能其前驱节点就是头结点,重新进行循环操作
                        (p = np).next = node;   // stale
                }
                //如果当前节点的前驱节点的状态为0 
                else if ((ps = p.status) == 0)
                    //将其当前节点的状态使用cas操作将其0替换为等待状态
                    U.compareAndSwapInt(p, WSTATUS, 0, WAITING); 
                //如果当前节点的前驱节点已经取消,重新设置当前节点的前驱节点
                else if (ps == CANCELLED) {
                    if ((pp = p.prev) != null) {
                        node.prev = pp;
                        pp.next = node;
                    }
                }
                else {
                    long time;
                    //如果超时时间为0,永久阻塞,直到调用UnSafe的unpark()方法
                    if (deadline == 0L)
                        time = 0L;
                    //如果当前时间已经过期,取消当前节点
                    else if ((time = deadline - System.nanoTime()) <= 0L)
                        //cancelWaiter方法可以看上面对此方法的介绍
                        return cancelWaiter(node, node, false);
                    //获取当前线程
                    Thread wt = Thread.currentThread();
                    //将其当前线程的监控对象设置为当前StampedLock,监控此线程被那个对象阻塞
                    U.putObject(wt, PARKBLOCKER, this);
                    //将其当前线程设置为当前队列中的线程
                    node.thread = wt;
                    //如果当前节点的前驱节点为等待状态,并且头尾节点不相等或者当前
                    //StampedLock的状态为写锁状态,并且头结点不变,当前节点的前驱节点不变
                    if (p.status < 0 &&
                        (p != h || (state & ABITS) == WBIT) &&
                        whead == h && node.prev == p)
                        //调用UnSafe的park方法阻塞当前线程
                        U.park(false, time);
                    //将其当前节点对应的线程置为空
                    node.thread = null;
                    //将其当前线程的监控对象置为空
                    U.putObject(wt, PARKBLOCKER, null);
                    //如果传入进来的参数interruptible为true,并且当前线程被中断 
                    if (interruptible && Thread.interrupted())
                        //取消当前节点,cancelWaiter方法可以看上面对此方法的介绍
                        return cancelWaiter(node, node, true);
                }
            }
        }
}

2.读锁的释放

//传入stamp进行读锁的释放
public void unlockRead(long stamp) {
        long s, m; WNode h;
        for (;;) {
            //传进来的stamp和当前stampedLock的state状态不一致,或者当前处于乐观读、
            //无锁状态,或者传进来的参数是乐观读、无锁的stamp,又或者当前状态为写锁状态,
            //抛出非法的锁状态异常
            if (((s = state) & SBITS) != (stamp & SBITS) ||
                (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
                throw new IllegalMonitorStateException();
            //如果当前StampedLock的state状态为读锁状态,并且读锁没有溢出
            if (m < RFULL) {
                //state使用cas进行减1操作
                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                    //如果减一操作成功,并且当前处于无锁状态,并且头结点不为空,
                    //并且头结点的状态为非0状态
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        //可以看上面对release方法的介绍 
                        release(h);
                    break;
                }
            }
            //可以看下面对tryDecReaderOverflow方法的介绍,如果不等于0直接退出,
            ///否则重新进行循环
            else if (tryDecReaderOverflow(s) != 0L)
                break;
        }
}

//读锁溢出进行减操作的方法
private long tryDecReaderOverflow(long s) {
        // assert (s & ABITS) >= RFULL;
        //如果当前状态s&上255等于126,当前读锁已满, 
        if ((s & ABITS) == RFULL) {
            //将其当前state状态设置为127
            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
                int r; long next;
                //如果设置成功,并且溢出的锁数目大于0,将其额外的读锁数进行减1操作
                if ((r = readerOverflow) > 0) {
                    readerOverflow = r - 1;
                    //将其next设置为传上来的state状态
                    next = s;
                }
                else
                    //如果当前溢出数为0,将其当前状态进行减1操作
                    next = s - RUNIT;
                 //将其state重新设置为next 
                 state = next;
                 return next;
            }
        }
        //如果当前线程的随机数&上7等于0,当前线程进行让步
        else if ((LockSupport.nextSecondarySeed() &
                  OVERFLOW_YIELD_RATE) == 0)
            //线程让步 
            Thread.yield();
        return 0L;
}

//无需传入stamp进行释放读锁
public boolean tryUnlockRead() {
        long s, m; WNode h;
        //如果当前状态处于读锁状态,而不是乐观读状态,或者无锁状态,或者写锁状态
        while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
            //如果当前state处于读锁状态,并且读锁没有溢出
            if (m < RFULL) {
                //stampedLock状态state使用cas进行减1操作,如果成功,跳出循环,直接返回
                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                    //如果操作成功,并且当前状态处于无锁状态,并且头结点不为空,
                    //及头结点的状态不为0
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        //看上面对release方法的介绍
                        release(h);
                    return true;
                }
            }
            //如果当前处于读锁模式,并且读锁溢出,使用上面介绍的tryDecReaderOverflow方法,
            //如果返回非0,直接退出,返回成功,否则重新进行循环
            else if (tryDecReaderOverflow(s) != 0L)
                return true;
        }
        return false;
}

乐观读获取、校验

1.乐观读的获取

public long tryOptimisticRead() {
        long s;
        //如果当前state不处于写模式返回s&SBITS,否则返回0失败
        return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}

2.乐观读的校验

public boolean validate(long stamp) {
        //在校验逻辑之前,会通过Unsafe的loadFence方法加入一个load内存屏障,目的是避免
        //copy变量到工作内存中和StampedLock.validate中锁状态校验运算发生重排序导致
        //锁状态校验不准确的问题
        U.loadFence();
        //如果传入进来的stamp & SBITS和state & SBITS相等
        return (stamp & SBITS) == (state & SBITS);
}

锁转换

1.转换为写锁

/**
     * state匹配stamp时, 执行下列操作之一. 
     *   1、stamp 已经持有写锁,直接返回.  
     *   2、读模式,但是没有更多的读取者,并返回一个写锁stamp.
     *   3、有一个乐观读锁,只在即时可用的前提下返回一个写锁stamp
     *   4、其他情况都返回0
     */
public long tryConvertToWriteLock(long stamp) {
        long a = stamp & ABITS, m, s, next;
        //如果传入进来的stamp和当前StampedLock的状态相同
        while (((s = state) & SBITS) == (stamp & SBITS)) {
            //如果当前处于无锁状态,或者乐观读状态
            if ((m = s & ABITS) == 0L) {
                //传入进来的stamp不处于无锁或者乐观读状态,直接退出,升级失败
                if (a != 0L)
                    break;
                //获取state使用cas进行写锁的获取,如果获取写锁成功直接退出
                if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
                    return next;
            }
            //如果当前stampedLock处于写锁状态
            else if (m == WBIT) {
                //传入进来的stamp不处于写锁状态,直接退出
                if (a != m)
                    break;
                //否则直接返回当前处于写锁状态的stamp
                return stamp;
            }
            //如果当前只有一个读锁,当前状态state使用cas进行减1加WBIT操作,
            //将其读锁升级为写锁状态
            else if (m == RUNIT && a != 0L) {
                if (U.compareAndSwapLong(this, STATE, s,
                                         next = s - RUNIT + WBIT))
                    return next;
            }
            //否则直接退出
            else
                break;
        }
        return 0L;
}

2.转换为读锁

/**
    *   state匹配stamp时, 执行下列操作之一.
        1、stamp 表示持有写锁,释放写锁,并持有读锁
		2、stamp 表示持有读锁 ,返回该读锁
		3、有一个乐观读锁,只在即时可用的前提下返回一个读锁stamp
        4、其他情况都返回0,表示失败
     *
     */
public long tryConvertToReadLock(long stamp) {
        long a = stamp & ABITS, m, s, next; WNode h;
        //如果传入进来的stamp和当前StampedLock的状态相同
        while (((s = state) & SBITS) == (stamp & SBITS)) {
            //如果当前StampedLock处于无锁状态或者乐观读状态
            if ((m = s & ABITS) == 0L) {
                //如果传入进来的stamp不处于无锁或者乐观读状态,直接退出,升级读锁失败
                if (a != 0L)
                    break;
                //如果当前StampedLock处于读锁、无锁或者乐观读状态,并且读锁数没有溢出
                else if (m < RFULL) {
                    //state使用cas操作进行加1操作,如果操作成功直接退出
                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                        return next;
                }
                //如果读锁溢出,调用tryIncReaderOverflow方法的溢出操作,
                //此方法可以看上面的介绍,如果返回非0,表明升级读锁成功,直接退出
                else if ((next = tryIncReaderOverflow(s)) != 0L)
                    return next;
            }
            //如果当前StampedLock的state处于写锁状态,如果锁升级成功,直接返回,否则重新循环
            else if (m == WBIT) {
                //传入进来的stamp不处于写锁状态
                if (a != m)
                    break;
                //释放写锁,上面在写锁的释放有解释,为什么不直接减去WBIT,再加上ORIGIN,
                //释放写锁加读锁
                state = next = s + (WBIT + RUNIT);
                //如果头结点不为空,并且头结点的状态不等于0 
                if ((h = whead) != null && h.status != 0)
                    //释放头结点的下一个有效节点,在上面release方法时有介绍
                    release(h);
                return next;
            }
            //如果传进来的stamp是读锁状态,直接返回传进来的stamp
            else if (a != 0L && a < WBIT)
                return stamp;
            //否则直接退出
            else
                break;
        }
        return 0L;
}

3.转换乐观读

//转换为乐观读
public long tryConvertToOptimisticRead(long stamp) {
        long a = stamp & ABITS, m, s, next; WNode h;
        //通过Unsafe的loadFence方法加入一个load内存屏障,目的是避免copy变量到工作内存中
        //和升级乐观读的中锁状态校验运算发生重排序导致锁状态校验不准确的问题    
        U.loadFence();
        for (;;) {
            //如果传入进来的stamp和当前的StampedLock的状态state不一致的话直接退出
            if (((s = state) & SBITS) != (stamp & SBITS))
                break;
            //如果当前处于无锁状态,或者乐观读状态
            if ((m = s & ABITS) == 0L) {
                //如果传入进来的stamp不是无锁或者乐观读状态直接退出
                if (a != 0L)
                    break;
                return s;
            }
            //如果当前处于写锁状态
            else if (m == WBIT) {
                //传入进来的stamp不是写锁状态,直接退出
                if (a != m)
                    break;
                //释放写锁,上面有解释为什么释放写锁的时候不是直接减去1再加上ORIGIN
                state = next = (s += WBIT) == 0L ? ORIGIN : s;
                //如果头结点不为空,并且头结点的状态不为0
                if ((h = whead) != null && h.status != 0)
                     //看上面release方法的介绍
                     release(h);
                return next;
            }
            //如果传入的进来stamp&上255等于0,或者大于写锁状态,直接退出
            else if (a == 0L || a >= WBIT)
                break;
            //如果读锁没有溢出,StampedLock的状态state使用cas进行-1操作
            else if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
                    //如果状态cas操作完,变为无锁,并且头结点不为空,以及头结点的状态不为0
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        //看上面release方法的介绍
                        release(h);
                    return next & SBITS;
                }
            }
            //如果读锁溢出,看上面对tryDecReaderOverflow方法的介绍
            else if ((next = tryDecReaderOverflow(s)) != 0L)
                return next & SBITS;
        }
        return 0L;
}

另外 StampedLock 的读写锁都是不可重入锁,所以当获取锁后释放锁前,不应该再调用会获取锁的操作,以避免产生死锁。

当多个线程同时尝试获取读锁和写锁的时候,谁先获取锁没有一定的规则,完全都是尽力而为,是随机的,并且该锁不是直接实现 Lock 或 ReadWriteLock 接口,而是内部自己维护了一个双向阻塞队列。
总结:StampedLock 提供的读写锁与 ReentrantReadWriteLock 类似,只是前者的都是不可重入锁。但是前者通过提供乐观读锁在多线程多读的情况下提供更好的性能,这是因为获取乐观读锁时候不需要进行 CAS 操作设置锁的状态,而只是简单的测试状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值