StampedLock 源码解读(jdk1.8)

2 篇文章 0 订阅

 

目录

介绍

重入性:不可重入 

 调度策略

改造后的CLH结构

字段介绍

写 

乐观读

锁转换 

释放锁


StampedLock是jdk1.8中新加入的一个锁。先看看它的介绍有一个大体的认识。

StampedLock和ReentrantReadWriteLock的区别

ReentrantReedWriteLock是基于AQS的,StampedLock是自己弄了一套类似CLH的结构。

ReentrantReedWriteLock的WriteLock支持Condition,StampedLock不支持。

ReentrantReedWriteLock支持重入,公平/非公平锁。StampedLock不支持。

但是StampedLock支持乐观读,各个锁之间的转换(虽然不一定能转成功)。ReentrantReadWriteLock只能由写锁转为读锁,它也只支持悲观读。

 

介绍

A capability-based lock with three modes for controlling read/write
  access.  The state of a StampedLock consists of a version and mode.
  Lock acquisition methods return a stamp that represents and
  controls access with respect to a lock state; "try" versions of
  these methods may instead return the special value zero to
  represent failure to acquire access. Lock release and conversion
  methods require stamps as arguments, and fail if they do not match
  the state of the lock. The three modes are:

它是一个基本能力锁,提供三种模式控制读写访问。StampedLock的state字段由一个版本号和模式组成(低七位为读的数量,低第八位是写状态,其他的为版本号)。获取锁的方法会返回一个代表和控制访问的标记。这些方法的"try"版本可能返回一个特殊的值0去代表获取访问失败。锁的释放和转换方法需要在获取锁的方法返回的stamps作为参数,如果这个stamp没有通过匹配方法将会抛出异常。

重入性:不可重入 

 <p>StampedLocks are designed for use as internal utilities in the
* development of thread-safe components. Their use relies on
* knowledge of the internal properties of the data, objects, and
* methods they are protecting.  They are not reentrant, so locked
* bodies should not call other unknown methods that may try to
* re-acquire locks (although you may pass a stamp to other methods
* that can use or convert it).  The use of read lock modes relies on
* the associated code sections being side-effect-free.  Unvalidated
* optimistic read sections cannot call methods that are not known to
* tolerate potential inconsistencies.  Stamps use finite
* representations, and are not cryptographically secure (i.e., a
* valid stamp may be guessable). Stamp values may recycle after (no
* sooner than) one year of continuous operation. A stamp held without
* use or validation for longer than this period may fail to validate
* correctly.  StampedLocks are serializable, but always deserialize
* into initial unlocked state, so they are not useful for remote
* locking.
 StampedLock被设计为使用作为内部工具在线程安全的组件开发。他们使用依赖他们保护的数据,方法,对象的
内部属性的了解。他们不是可重入的,因此被锁定的部分不能去调用其他未知的方法,应该去重新获取锁(即使你
可能传一个标记——stamp去其他方法能使用或者转换它)。读锁模式的使用依赖相联系的代码片段是无副作用的。
没有校验的乐观读片段不能调用不知道容忍潜在矛盾(不一致)的方法。Stamps使用有限的表示并且没有加密。
标记值可能循环使用在连续运作的一年(不早于)后。一个stamp没有使用或验证的时长超过这个周期可能无法正
确验证。StampedLock也是可以被序列化的但是在反序列化是给默认的没有锁定的状态,因此他们不能被用来做远
程锁定。

 

 调度策略

<p>The scheduling policy of StampedLock does not consistently
* prefer readers over writers or vice versa.  All "try" methods are
* best-effort and do not necessarily conform to any scheduling or
* fairness policy. A zero return from any "try" method for acquiring
* or converting locks does not carry any information about the state
* of the lock; a subsequent invocation may succeed.
StampedLock的调度策略不是固定的,对读和写没有偏爱关系(谁抢到是谁的)。所有的try方法是尽最大努力去
获取锁,不需要遵守任何调度或公平策略。try 获取/转换方法返回0没有携带关于锁状态任何信息。随后的一个
调用可能成功。

改造后的CLH结构

读锁请求和写锁请求的入队方式如图所示。

当请求一个写锁时如果前面有其他锁节点会将当前节点赋值给前节点的next字段。

当请求一个读锁时如果前面是读锁节点则会赋值给它的cowait字段。

当请求一个读锁时如果前面是写锁节点则会赋值给它的next字段。

可以很明显的看出读节点会形成一个栈。

没有使用AQS,StampedLock使用另外一套结构,使并发的性能更上一层楼。后面的读线程会帮助前面阻塞的读线程解除阻塞,使读的性能更高。

字段介绍

/** Number of processors, for spin control */
/**处理器的数量,用于旋转控制*/
private static final int NCPU = Runtime.getRuntime().availableProcessors();

/** Maximum number of retries before enqueuing on acquisition */
/**在请求入队获得前最大的重试次数*/
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;

/** Maximum number of retries before blocking at head on acquisition */
/**阻塞在头部获得前最大的重试次数 */
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;

/** Maximum number of retries before re-blocking */
/**重复阻塞前最大的重试次数*/
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;

/** The period for yielding when waiting for overflow spinlock */
/** */
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1

/** The number of bits to use for reader count before overflowing */
/** 在溢出前读线程的bit数*/
private static final int LG_READERS = 7;

// Values for lock state and stamp operations 一个读锁单位
private static final long RUNIT = 1L;
//左移七位 10000000
//WBIT 二进制为10000000 一个写锁单位
private static final long WBIT  = 1L << LG_READERS;
//二进制位七个1
//RBITS 二进制位 1111111
private static final long RBITS = WBIT - 1L;
//RFULL 二进制为 1111110 用来检验读锁的次有数量是不是快溢出了
private static final long RFULL = RBITS - 1L;
//ABITS的二进制为 11111111 八个1 用来检验是否有锁
private static final long ABITS = RBITS | WBIT;
//SBITS的二进制为 0000000
private static final long SBITS = ~RBITS; // note overlap with ABITS

// Initial value for lock state; avoid failure value zero
//锁状态初始化值,避免失败的值为0
//左移一位 ORIGIN的二进制为100000000
private static final long ORIGIN = WBIT << 1;

// Special value from cancelled acquire methods so caller can throw IE
//取消获取方法的特定值因此调用者可以抛出中断异常
private static final long INTERRUPTED = 1L;

// Values for node status; order matters
private static final int WAITING   = -1;
private static final int CANCELLED =  1;

// Modes for nodes (int not boolean to allow arithmetic)
private static final int RMODE = 0;
private static final int WMODE = 1;
/** Head of CLH queue */
private transient volatile WNode whead;
/** Tail (last) of CLH queue */
private transient volatile WNode wtail;

// views
transient ReadLockView readLockView;
transient WriteLockView writeLockView;
transient ReadWriteLockView readWriteLockView;

/** Lock sequence/state */
private transient volatile long state;
/** extra reader count when state read count saturated */
//如果读超过了2的七次方,会将溢出的部分存放到这个字段
private transient int readerOverflow;

写 

*  <li><b>Writing.</b> Method {@link #writeLock} possibly blocks
*   waiting for exclusive access, returning a stamp that can be used
*   in method {@link #unlockWrite} to release the lock. Untimed and
*   timed versions of {@code tryWriteLock} are also provided. When
*   the lock is held in write mode, no read locks may be obtained,
*   and all optimistic read validations will fail.  </li>
写。writeLock方法可能需要阻塞等待为了独占访问,返回一个标记(stamp)能在unlockWrite方法中使用去释
放锁。超时和非超时的tryWriteLock也同样支持。当锁被写模式持有时,写锁是不能获得成功的,并且所有的乐
观读校验都会失败。

写主要方法: 

public long writeLock() {
        long s, next;  // bypass acquireWrite in fully unlocked case only
        //低8位都是0,低七位为读锁,第八位开始为写锁,如果&ABITS为0则说明没有读锁和写锁,
        //如果CAS成功返回的next为当前状态加上一个写的子单位,
        return ((((s = state) & ABITS) == 0L &&
                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
                next : acquireWrite(false, 0L));
    }
private long acquireWrite(boolean interruptible, long deadline) {
        //p是尾指针,如果尾指针是null会初始化头尾指针,node的新的尾指针,也就是p是node的前一个节点
        WNode node = null, p;
        for (int spins = -1;;) { // spin while enqueuing
            long m, s, ns;
            //如果state的低8位为0,说明没有被加读或写锁,直接上锁
            if ((m = (s = state) & ABITS) == 0L) {
                //CAS成功
                if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
                    return ns;
            }
            //如果spins为负数
            else if (spins < 0)
                //进入这里说明锁被其他线程持有(因为上一个判断没有生效),计算自旋的次数,如果state等于WBIT(也就是一次的加锁的值)
                // 且CLH锁的首尾相等则spins的值等于自旋的最大值,否则等于0
                //如果这个写锁是CLH等待队列的第一个则自旋,否则不进行自旋避免浪费cpu时间
                spins = (m == WBIT && wtail == whead) ? SPINS : 0;
            //当自旋大于0,
            else if (spins > 0) {
                //取得一个与线程无关的随机数
                if (LockSupport.nextSecondarySeed() >= 0)
                    --spins;
            }
            else if ((p = wtail) == null) { // initialize queue
                //尾指针为空,初始化CLH队列
                WNode hd = new WNode(WMODE, null);
                if (U.compareAndSwapObject(this, WHEAD, null, hd))
                    wtail = hd;
            }
            else if (node == null)
                //如果node为空则初始化
                node = new WNode(WMODE, p);
            else if (node.prev != p)
                node.prev = p;
            //把并放入CLH队列尾,退出
            else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
                p.next = node;
                break;
            }
        }

        //上面主要是完成了node的初始化,如果写锁被占用则将node放入CLH队列的末尾。此时的node没有对thread赋值。
        //wtail的值是node,p的值是以前的wtail(可能是刚初始化的)

        for (int spins = -1;;) {
            WNode h, np, pp; int ps;
            //如果首尾指针相等,首尾说明队列里没有值
            //这里的p虽然是以前被赋值为尾节点,但是它也是node当前节点的前驱节点
            //如果前驱节点变成头节点了,
            if ((h = whead) == p) {
                if (spins < 0)
                    spins = HEAD_SPINS;
                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;
                        }
                    }
                    else if (LockSupport.nextSecondarySeed() >= 0 &&
                             --k <= 0)
                        break;
                }
            }
            //h指向了首指针
            else if (h != null) { // help release stale waiters
                WNode c; Thread w;
                //如果首指针指向的节点是读节点,就协助读操作
                while ((c = h.cowait) != null) {
                    if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                        (w = c.thread) != null)
                        U.unpark(w);
                }
            }
            //h等于首指针
            if (whead == h) {
                if ((np = node.prev) != p) {
                    if (np != null)
                        (p = np).next = node;   // stale
                }
                //如果当前节点的前一个节点的状态是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 argument to park means no timeout
                    if (deadline == 0L)
                        time = 0L;
                    else if ((time = deadline - System.nanoTime()) <= 0L)
                        return cancelWaiter(node, node, false);
                    Thread wt = Thread.currentThread();
                    U.putObject(wt, PARKBLOCKER, this);
                    //把当前线程放入node
                    node.thread = wt;
                    //前一个节点是否是等待状态且(p不等于头指针或有被读锁或者写锁锁定)且
                    //h==头指针且当前节点的前一个节点是p
                    if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
                        whead == h && node.prev == p)
                        //阻塞当前线程
                        U.park(false, time);  // emulate LockSupport.park
                    node.thread = null;
                    U.putObject(wt, PARKBLOCKER, null);
                    if (interruptible && Thread.interrupted())
                        return cancelWaiter(node, node, true);
                }
            }
        }
    }

 

<li><b>Reading.</b> Method {@link #readLock} possibly blocks
*   waiting for non-exclusive access, returning a stamp that can be
*   used in method {@link #unlockRead} to release the lock. Untimed
*   and timed versions of {@code tryReadLock} are also provided. </li>
读,readLock方法可能阻塞等待非独占访问,返回一个能在方法unlockRead中使用去释放锁的标记stamp.非计时
和计时的版本的tryReadLock也都提供了。

读主要方法:

 public long readLock() {
        long s = state, next;  // bypass acquireRead on common uncontended case
        //判断是否有写锁((s & ABITS) < RFULL)state和八个1做与运算如果小于RFULL说明没有写锁
        return ((whead == wtail && (s & ABITS) < RFULL &&
                 U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
                next : acquireRead(false, 0L));
    }
private long acquireRead(boolean interruptible, long deadline) {
        WNode node = null, p;
        for (int spins = -1;;) {
            WNode h;
            //如果头指针等于尾指针
            if ((h = whead) == (p = wtail)) {
                for (long m, s, ns;;) {
                    //如果当前锁状态小于(读的最大数 - 1)且CAS操作成功则返回,否则
                    if ((m = (s = state) & ABITS) < RFULL ?
                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                            //如果锁状态正好等于读的最大数-1,将锁状态设置为读的最大值成功会返回读的最大值,失败则返回0
                            //如果不是则可能会线程让步(和一个随机数有关)
                        (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
                        return ns;
                    //说明有写锁,需要自旋
                    else if (m >= WBIT) {
                        if (spins > 0) {
                            if (LockSupport.nextSecondarySeed() >= 0)
                                --spins;
                        }
                        else {
                            //自旋次数使用完后头指针等于h且尾指针等于p,说明自旋次数用完后没有任务进入也没有任务执行完
                            //或首不能尾
                            // 则跳出循环
                            if (spins == 0) {
                                WNode nh = whead, np = wtail;
                                if ((nh == h && np == p) || (h = nh) != (p = np))
                                    break;
                            }
                            //初始化自旋次数(1左移六位,100万次)
                            spins = SPINS;
                        }
                    }
                }
            }
            //如果尾指针为空则初始化CLH队列
            if (p == null) { // initialize queue
                WNode hd = new WNode(WMODE, null);
                if (U.compareAndSwapObject(this, WHEAD, null, hd))
                    wtail = hd;
            }
            //如果node为空则初始化node,RMODE模式
            else if (node == null)
                node = new WNode(RMODE, p);
            //如果首尾指针相等或者尾指针是写模式
            else if (h == p || p.mode != RMODE) {
                if (node.prev != p)
                    node.prev = p;
                    //将node放入CLH队列的末尾
                else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
                    p.next = node;
                    //退出循环
                    break;
                }
            }
            //这里的操作是将node指针赋值给尾节点的cowait字段并将以前尾节点的cowait字段赋值给node的cowait形成链
            // (如果前一个else-if成功是不会进入这里的)
            else if (!U.compareAndSwapObject(p, WCOWAIT,
                                             node.cowait = p.cowait, node))
                //如果操作失败还原node的cowait字段
                node.cowait = null;
            else {
                for (;;) {
                    WNode pp, c; Thread w;
                    if ((h = whead) != null && (c = h.cowait) != null &&
                        U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                        (w = c.thread) != null) // help release
                        //只有首尾节点不一致才会到这来,所以这里吧头节点的cowait出栈协助读不会影响上个else-if操作插入的值
                        U.unpark(w);
                    //如果首指针等于当前节点的前前节点或者头指针等于当前节点的前节点或当前节点的前前节点为null
                    if (h == (pp = p.prev) || h == p || pp == null) {
                        long m, s, ns;
                        do {
                            //如果前面没有写锁则一直循环直到成功
                            if ((m = (s = state) & ABITS) < RFULL ?
                                U.compareAndSwapLong(this, STATE, s,
                                                     ns = s + RUNIT) :
                                (m < WBIT &&
                                 (ns = tryIncReaderOverflow(s)) != 0L))
                                return ns;
                        } while (m < WBIT);
                    }
                    //如果首节点和前前节点没有改变
                    if (whead == h && p.prev == pp) {
                        long time;
                        //前前节点为null或头节点等于当前节点的前一个节点或者前一个节点已经被取消
                        if (pp == null || h == p || p.status > 0) {
                            node = null; // throw away
                            break;
                        }
                        //判断是否超时
                        if (deadline == 0L)
                            time = 0L;
                        else if ((time = deadline - System.nanoTime()) <= 0L)
                            return cancelWaiter(node, p, false);
                        //如果前面是写节点则会阻塞当前线程。
                        Thread wt = Thread.currentThread();
                        U.putObject(wt, PARKBLOCKER, this);
                        node.thread = wt;
                        if ((h != pp || (state & ABITS) == WBIT) &&
                            whead == h && p.prev == pp)
                            U.park(false, time);
                        node.thread = null;
                        U.putObject(wt, PARKBLOCKER, null);
                        if (interruptible && Thread.interrupted())
                            return cancelWaiter(node, p, true);
                    }
                }
            }
        }

        //前面有获取写锁节点会到这里来

        for (int spins = -1;;) {
            WNode h, np, pp; int ps;
            //如果首尾节点相等
            if ((h = whead) == p) {
                //在头节点自旋次数
                if (spins < 0)
                    spins = HEAD_SPINS;
                //如果自旋次数小于最大在头部自旋的次数自旋次数左移一位
                else if (spins < MAX_HEAD_SPINS)
                    spins <<= 1;
                //在头结点自旋
                for (int k = spins;;) { // spin at head
                    long m, s, ns;
                    if ((m = (s = state) & ABITS) < RFULL ?
                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                        (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
                        WNode c; Thread w;
                        whead = node;
                        node.prev = null;
                        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;
                //协助头节点进行读操作(如果头节点是读节点)
                while ((c = h.cowait) != null) {
                    if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                        (w = c.thread) != null)
                        U.unpark(w);
                }
            }
            //如果头结点仍然是h,说明头结点是写锁
            if (whead == h) {
                //如果当前节点的前一个节点不是尾节点(说明前面没有写节点)
                if ((np = node.prev) != p) {
                    if (np != null)//如果等于node的前一个节点的指针null说明前面没有写节点
                        //如果前一个节点存在,则将p的值赋值为np,并将p的下一个节点指向node
                        (p = np).next = node;   // stale
                }
                //如果尾节点的状态是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;
                    if (deadline == 0L)
                        time = 0L;
                    else if ((time = deadline - System.nanoTime()) <= 0L)
                        return cancelWaiter(node, node, false);
                    Thread wt = Thread.currentThread();
                    U.putObject(wt, PARKBLOCKER, this);
                    node.thread = wt;
                    if (p.status < 0 &&
                        (p != h || (state & ABITS) == WBIT) &&
                        whead == h && node.prev == p)
                        U.park(false, time);
                    node.thread = null;
                    U.putObject(wt, PARKBLOCKER, null);
                    if (interruptible && Thread.interrupted())
                        return cancelWaiter(node, node, true);
                }
            }
        }
    }

下面的两个方法是在读锁数超过了2的7次方时进行的操作。 

/**
     * Tries to decrement readerOverflow.
     * 尝试去减少读溢出
     * @param s a reader overflow stamp: (s & ABITS) >= RFULL
     * @return new stamp on success, else zero
     */
    private long tryDecReaderOverflow(long s) {
        // assert (s & ABITS) >= RFULL;
        if ((s & ABITS) == RFULL) {
            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
                int r; long next;
                if ((r = readerOverflow) > 0) {
                    //如果读溢出字段大于0,说明读的数超过了2的七次方,多余的在readerOverflow计数。
                    readerOverflow = r - 1;
                    next = s;
                }
                else
                    //没有溢出,则state直接减1个读单位
                    next = s - RUNIT;
                 state = next;
                 return next;
            }
        }
        //一个随机数,如果计算为0则进行线程让步
        else if ((LockSupport.nextSecondarySeed() &
                  OVERFLOW_YIELD_RATE) == 0)
            Thread.yield();
        return 0L;
    }

private long tryIncReaderOverflow(long s) {
        // assert (s & ABITS) >= RFULL;
        if ((s & ABITS) == RFULL) {
            //设置为RBITS(七个1,也就是读状态的最大值)
            if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
                ++readerOverflow;
                state = s;
                return s;
            }
        }
        else if ((LockSupport.nextSecondarySeed() &
                  OVERFLOW_YIELD_RATE) == 0)
            Thread.yield();
        return 0L;
    }

 

 

 

乐观读

<li><b>Optimistic Reading.</b> Method {@link #tryOptimisticRead}
*   returns a non-zero stamp only if the lock is not currently held
*   in write mode. Method {@link #validate} returns true if the lock
*   has not been acquired in write mode since obtaining a given
*   stamp.  This mode can be thought of as an extremely weak version
*   of a read-lock, that can be broken by a writer at any time.  The
*   use of optimistic mode for short read-only code segments often
*   reduces contention and improves throughput.  However, its use is
*   inherently fragile.  Optimistic read sections should only read
*   fields and hold them in local variables for later use after
*   validation. Fields read while in optimistic mode may be wildly
*   inconsistent, so usage applies only when you are familiar enough
*   with data representations to check consistency and/or repeatedly
*   invoke method {@code validate()}.  For example, such steps are
*   typically required when first reading an object or array
*   reference, and then accessing one of its fields, elements or
*   methods. </li>

 乐观读。方法tryOptimisticRead会在锁不是写状态下返回一个非0的标记(stamp),否则会返回0。如果在获得标记(stamp)后锁没有被写次有,方法validate返回true.这个模式能被认为是一个读锁的极度弱的版本。它能在任何时候被一个写锁打破。乐观锁模式的使用对短只读的代码片段通常能减少竞争和提升吞吐量。然而,它的使用天生是容易打破的。乐观读片段应该只读字段和持有他们在本地变量稍后使用在检验通过后。在乐观模式下读取字段可能很不一致,因此使用申请只有当你很熟悉数据表示去检查一致性反复的调用方法validate()。例如:先访问一个对象或数组的引用,然后访问一个他们的字段,元素或方法。

/**
     * Returns true if the lock has not been exclusively acquired
     * since issuance of the given stamp. Always returns false if the
     * stamp is zero. Always returns true if the stamp represents a
     * currently held lock. Invoking this method with a value not
     * obtained from {@link #tryOptimisticRead} or a locking method
     * for this lock has no defined effect or result.
     *
     * 如果版本号是0则会一直返回false,因为版本号的默认值是1.
     *
     * @param stamp a stamp
     * @return {@code true} if the lock has not been exclusively acquired
     * since issuance of the given stamp; else false
     */
    public boolean validate(long stamp) {
        U.loadFence();
        //判断版本号是否一致
        return (stamp & SBITS) == (state & SBITS);
    }
public long tryOptimisticRead() {
        long s;
        //如果有写锁就返回0,如果只有读锁则将低七位置0,只返回版本号
        return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
    }

 

 

 

锁转换 

 <p>This class also supports methods that conditionally provide
    conversions across the three modes. For example, method {@link
    #tryConvertToWriteLock} attempts to "upgrade" a mode, returning
    a valid write stamp if (1) already in writing mode (2) in reading
    mode and there are no other readers or (3) in optimistic mode and
    the lock is available. The forms of these methods are designed to
    help reduce some of the code bloat that otherwise occurs in
    retry-based designs.
  这个类也支持有条件的提供三种模式的相互转换。比如方法tryConvertToWriteLock尝试
  去升级一个模式,返回一个有效写stamp如果1.已经在写模式,2.在读模式并且没有其他的读,3,在乐观
  模式并且锁是可获得的。这些方法的表单被设计去帮助减少许多过量代码否则重复retry-based的设计。
 public long tryConvertToOptimisticRead(long stamp) {
        long a = stamp & ABITS, m, s, next; WNode h;
        //避免重排序
        U.loadFence();
        for (;;) {
            if (((s = state) & SBITS) != (stamp & SBITS))
                break;
            if ((m = s & ABITS) == 0L) {
                if (a != 0L)
                    break;
                return s;
            }
            else if (m == WBIT) {
                if (a != m)
                    break;
                //更改版本号,并释放写锁节点
                state = next = (s += WBIT) == 0L ? ORIGIN : s;
                if ((h = whead) != null && h.status != 0)
                    release(h);
                return next;
            }
            else if (a == 0L || a >= WBIT)
                break;
            //如果没有超过2的七次方的读数
            else if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) {
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        release(h);
                    return next & SBITS;
                }
            }
            //超过了2的七次方则存在另外的溢出字段
            else if ((next = tryDecReaderOverflow(s)) != 0L)
                return next & SBITS;
        }
        return 0L;
    }

/**
     * If the lock state matches the given stamp, performs one of
     * the following actions. If the stamp represents holding a write
     * lock, returns it.  Or, if a read lock, if the write lock is
     * available, releases the read lock and returns a write stamp.
     * Or, if an optimistic read, returns a write stamp only if
     * immediately available. This method returns zero in all other
     * cases.
     * 如果锁状态版本号匹配给定的版本号,执行下面的行为:
     * 1:如果版本号代表持有一个写锁,直接返回这个版本号
     * 2:如果一个读锁,如果写锁是是可获得的,释放读锁并且返回一个写锁的版本号
     * 3:如果一个乐观的读,返回一个写的版本号只有立刻能获取到写锁。
     * 这个方法返回0在所有其他情况。
     * @param stamp a stamp
     * @return a valid write stamp, or zero on failure
     */
    public long tryConvertToWriteLock(long stamp) {
        long a = stamp & ABITS, m, s, next;
        while (((s = state) & SBITS) == (stamp & SBITS)) {
            if ((m = s & ABITS) == 0L) {
                if (a != 0L)
                    break;
                if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT))
                    return next;
            }
            else if (m == WBIT) {
                if (a != m)
                    break;
                return stamp;
            }
            else if (m == RUNIT && a != 0L) {
                if (U.compareAndSwapLong(this, STATE, s,
                                         next = s - RUNIT + WBIT))
                    return next;
            }
            else
                break;
        }
        return 0L;
    }

    /**
     * If the lock state matches the given stamp, performs one of
     * the following actions. If the stamp represents holding a write
     * lock, releases it and obtains a read lock.  Or, if a read lock,
     * returns it. Or, if an optimistic read, acquires a read lock and
     * returns a read stamp only if immediately available. This method
     * returns zero in all other cases.
     * 如果锁状态版本号匹配给定的版本号,执行下面的行为:
     * 1:版本号代表持有一个写锁,释放它并获得一个读锁
     * 2:如果一个读锁返回它
     * 3:如果是一个乐观读,请求一个读锁并返回读的版本号只有在能立即获得锁的情况下
     *
     * 方法返回0在其他所有情况
     *
     * @param stamp a stamp
     * @return a valid read stamp, or zero on failure
     */
    public long tryConvertToReadLock(long stamp) {
        long a = stamp & ABITS, m, s, next; WNode h;
        while (((s = state) & SBITS) == (stamp & SBITS)) {
            if ((m = s & ABITS) == 0L) {
                if (a != 0L)
                    break;
                else if (m < RFULL) {
                    if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                        return next;
                }
                else if ((next = tryIncReaderOverflow(s)) != 0L)
                    return next;
            }
            else if (m == WBIT) {
                if (a != m)
                    break;
                state = next = s + (WBIT + RUNIT);
                if ((h = whead) != null && h.status != 0)
                    release(h);
                return next;
            }
            else if (a != 0L && a < WBIT)
                return stamp;
            else
                break;
        }
        return 0L;
    }

 

释放锁

 /**
     * If the lock state matches the given stamp, releases the
     * corresponding mode of the lock.
     * 如果锁的状态和给的状态相匹配,释放相应模式下的锁
     * @param stamp a stamp returned by a lock operation
     * @throws IllegalMonitorStateException if the stamp does
     * not match the current state of this lock
     */
    public void unlock(long stamp) {
        long a = stamp & ABITS, m, s; WNode h;
        //比较高位的版本号是否一致
        while (((s = state) & SBITS) == (stamp & SBITS)) {
            //如果state没有锁则跳出循环,抛出异常
            if ((m = s & ABITS) == 0L)
                break;
            //如果是写模式
            else if (m == WBIT) {
                if (a != m)
                    break;
                //更改版本号
                state = (s += WBIT) == 0L ? ORIGIN : s;
                if ((h = whead) != null && h.status != 0)
                    release(h);
                return;
            }
            //a的值是读写锁的加锁状态,如果为0,或者有读写锁同时存在说明有问题。
            else if (a == 0L || a >= WBIT)
                break;
            //如果小于读的最大数减一
            else if (m < RFULL) {
                //读数减1,
                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                    //释放头节点
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        release(h);
                    return;
                }
            }
            else if (tryDecReaderOverflow(s) != 0L)
                return;
        }
        throw new IllegalMonitorStateException();
    }

 

public boolean tryUnlockWrite() {
        long s; WNode h;
        if (((s = state) & WBIT) != 0L) {
            //修改版本号(实质就是版本号加1)
            state = (s += WBIT) == 0L ? ORIGIN : s;
            //释放写节点
            if ((h = whead) != null && h.status != 0)
                release(h);
            return true;
        }
        return false;
    }
 public boolean tryUnlockRead() {
        long s, m; WNode h;
        while ((m = (s = state) & ABITS) != 0L && m < WBIT) {
            if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        release(h);
                    return true;
                }
            }
            else if (tryDecReaderOverflow(s) != 0L)
                return true;
        }
        return false;
    }

 

/**
     * Wakes up the successor of h (normally whead). This is normally
     * just h.next, but may require traversal from wtail if next
     * pointers are lagging. This may fail to wake up an acquiring
     * thread when one or more have been cancelled, but the cancel
     * methods themselves provide extra safeguards to ensure liveness.
     * 唤醒头结点的接任者。这个通常是h的next指针,但是可能需要从尾节点遍历如果
     * next节点是失效的。这个可能唤醒失败请求锁的线程当一个或多个被取消,但是取消
     * 方法他们提供额外安全的保活性
     *
     * 这个方法没有改变head的值,head的值是在acquireWrite和acquireRead方法中
     * 当当前节点的前驱节点等于头结点且没有锁时,将头结点赋值为当前节点
     */
    private void release(WNode h) {
        if (h != null) {
            WNode q; Thread w;
            U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
            //如果head的next为空,则从尾节点开始遍历可用的
            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);
        }
    }

 

参考资料:

jdk源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值