java锁(7)改进读写锁StampedLock详解

1、StampedLock特性

StampedLock是JDK 8新增的读写锁,跟读写锁不同,它并不是由AQS实现的。它的state为一个long型变量,状态的设计也不同于读写锁,且提供了三种模式来控制 read/write 的获取,并且内部实现了自己的同步等待队列。

1.1、StampedLock读写锁

写锁:使用writeLock方法获取,当锁不可用时会阻塞,获取成功后返回一个与这个写锁对应的stamp,在unlockWrite方法中,需要通过这个stamp来释放与之对应的锁。在tryWriteLock同样也会提供这个stamp。当在write模式中获取到写锁时,读锁不能被获取,并且所有的乐观读锁验证(validate方法)都会失败。
读锁:使用readLock方法获取,当超出可用资源时(类似AQS的state设计)会阻塞。同样的,在获取锁成功后也会返回stamp,作用与上述相同。tryReadLock同样如此。
乐观读锁:使用tryOptimisticRead方法获取,只有在写锁可用时才能成功获取乐观读锁,获取成功后也会返回一个stamp。validate方法可以根据这个stamp来判断写锁是否被获取。这种模式可以理解为一个弱化的读锁(weak version of a read-lock),它在任何时候都能被破坏。乐观读模式常被用在短的只读的代码段,用来减少争用并提高吞吐量。乐观读区域应该只读取字段,并将它们保存在本地变量中,以便在验证(validate方法)后使用。在乐观读模式中字段的读取可能会不一致,所以可能需要反复调用validate()来检查一致性。例如,当首次读取一个对象或数组引用,然后访问其中一个的字段、元素或方法时,这些步骤通常是必需的。

1.2、三种模式的转换

StampedLock可以将三种模式是锁进行有条件的互相转换。
将其他锁转换为写锁tryConvertToWriteLock():
当前邮戳为持有写锁模式,直接返回当前的邮戳;
当前邮戳为持有读锁模式,则会释放读锁并获取写锁,并返回写锁邮戳;
当前邮戳持有乐观锁,通过CAS立即获取写锁,成功则返回写锁邮戳;失败则返回0;

将其他锁转换为读锁tryConvertToReadLock:
当前邮戳为持有写锁模式,则会释放写锁并获取读锁,并返回读锁邮戳;
当前邮戳为持有读锁模式,则直接返回当前读锁邮戳;
当前邮戳持有乐观锁,通过CAS立即获取读锁,则返回读锁邮戳;否则,获取失败返回0;

将其他锁转换为乐观锁tryConvertToOptimisticRead:
当前邮戳为持有读或写锁,则直接释放读写锁,并返回释放后的观察者邮戳值;
当前邮戳持有乐观锁,若乐观锁邮戳有效,则返回观察者邮戳;

1.3、StampedLock的应用场景

StampedLock一般作为线程安全的内部工具类。它的使用依赖于对数据、对象和方法的内部属性有一定的了解。StampedLock 是不可重入的,所以在锁的内部不能调用其他尝试重复获取锁的方。一个stamp如果在很长时间都没有使用或验证,在很长一段时间之后可能就会验证失败。StampedLocks是可序列化的,但是反序列化后变为初始的非锁定状态,所以在远程锁定中是不安全的。

1.4、StampedLock的公平性

StampedLock 的调度策略不会始终偏向读线程或写线程,所有的"try"方法都是尽最大努力获取,并不一定遵循任何调度或公平策略。从"try"方法获取或转换锁失败返回0时,不会携带任何锁的状态信息。由于StampedLock支持跨多个锁模式的协调使用,它不会直接实现Lock或ReadWriteLock接口。但是,如果应用程序需要Lock的相关功能,它可以通过asReadLock()、asWriteLock()和asReadWriteLock()方法返回一个Lock视图。

2、源码分析

2.1、主要属性

//获取CPU的可用线程数量,用于确定自旋的时候循环次数
private static final int NCPU = Runtime.getRuntime().availableProcessors();

//根据NCPU确定自旋的次数限制(并不是一定这么多次,因为实际代码中是随机的)
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;

//头节点上的自旋次数
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;

//头节点上的最大自旋次数
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;

//等待自旋锁溢出的周期数
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1

//在溢出之前读线程计数用到的bit位数
private static final int LG_READERS = 7;

//一个读状态单位:0000 0000 0001
private static final long RUNIT = 1L;
//一个写状态单位:0000 1000 0000
private static final long WBIT  = 1L << LG_READERS;
//读状态标识:0000 0111 1111
private static final long RBITS = WBIT - 1L;
//读锁计数的最大值:0000 0111 1110
private static final long RFULL = RBITS - 1L;
//读线程个数和写线程个数的掩码:0000 1111 1111
private static final long ABITS = RBITS | WBIT;
//// 读线程个数的反数,高25位全部为1:1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1000 0000
private static final long SBITS = ~RBITS; // note overlap with ABITS

// state的初始值
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;


//等待队列的头节点
private transient volatile WNode whead;
//等待队列的尾节点
private transient volatile WNode wtail;

// 锁状态
private transient volatile long state;
////因为读状态只有7位很小,所以当超过了128之后将使用此变量来记录
private transient int readerOverflow;

2.2、node节点实现

//等待队列的节点实现
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; }
}

2.3、state状态实现

state状态说明:

  • bit0—bit6为作为读锁计数,当超出RFULL(126)时,用readerOverflow作为读锁计数。当获取到读锁时,state加RUINT(值为1);当释放读锁时,state减去RUINT。
  • bit7为写锁标识,其值为1表示已获取写锁,值为0表示已获取读锁。当线程获取写锁或释放写锁时,都会将state加WBIT;
  • bit8—bit64:表示写锁版本,不论获取写锁还是释放写锁,其值都会改变。
    初始状态bit8为1,其他位为0。

state常用状态标识:

  • RUNIT:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
  • WBIT:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1000 0000
  • RBITS:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0111 1111
  • RFULL:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0111 1110
  • ABITS:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1111 1111
  • SBITS:
    1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1000 0000
  • ORIGIN:
    0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001 0000 0000

state常用状态判断:

  • 有无线程获取写状态:state < WBIT,true:无,false:有;
  • 读状态是否溢出:(state & ABITS) < RFULL,true;否;false:是;
  • 获取读状态: state + RUNIT(或者readerOverflow + 1)
  • 获取写状态: state + WBIT
  • 释放读状态: state - RUNIT(或者readerOverflow - 1)
  • 释放写状态: (s += WBIT) == 0L ? ORIGIN : s
  • 是否为写锁: (state & WBIT) != 0L
  • 是否为读锁: (state & RBITS) != 0L

2.4、写锁获取及释放

获取写锁:

public long writeLock() {
    long s, next;  
    //(state & ABITS)获取低8位,判断有无读/写锁存在,
    //有其他读锁则bit0-bit6不为0,有其他写锁则bit7不为0,
    //因此如果有别的写锁或者读锁存在将失败
    //尝试CAS获取写锁
    //失败调用acquireWrite继续获取写锁
    return ((((s = state) & ABITS) == 0L &&
             U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
            next : acquireWrite(false, 0L));
}
private long acquireWrite(boolean interruptible, long deadline) {
    //node为当前节点,p为当前节点的前驱节点
    WNode node = null, p;
    
    // 第一次自旋——主要进行入队工作
    for (int spins = -1;;) { // spin while enqueuing
        long m, s, ns;
        //(state&ABITS)为0表示无读/写锁,则尝试CAS获取写锁
        if ((m = (s = state) & ABITS) == 0L) {
            if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
                return ns;
        }
        else if (spins < 0)
            // 如果自旋次数小于0,则计算自旋的次数
            // 如果当前有写锁(m == WBIT)独占,且队列无元素(wtail == whead),
            // 说明当前节点的前驱节点为获取独占锁的节点,锁很快就会释放,
            // 就自旋SPINS次就行了,如果自旋完了还没轮到自己才入队
            // 否则自旋次数为0
            spins = (m == WBIT && wtail == whead) ? SPINS : 0;
        else if (spins > 0) {
            // 当自旋次数大于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;
        }
    }

    // 第二次自旋——主要进行阻塞并等待唤醒
    for (int spins = -1;;) {
        // h为头节点,np为新增节点的前置节点,pp为前前置节点,ps为前置节点的状态
        WNode h, np, pp; int ps;
        // 如果头节点等于前置节点,说明快轮到自己了
        if ((h = whead) == p) {
            if (spins < 0)
                // 自旋次数小于0,则初始化自旋次数
                spins = HEAD_SPINS;
            else if (spins < MAX_HEAD_SPINS)
                // 自旋次数小于头结点最大自旋次数,则增加自旋次数
                spins <<= 1;
                
            // 第三次自旋,不断尝试获取写锁    
            for (int k = spins;;) { // spin at head
                long s, ns;
                //无读写锁则CAS获取写锁,成功则更新节点信息
                if (((s = state) & ABITS) == 0L) {
                    if (U.compareAndSwapLong(this, STATE, s,
                                             ns = s + WBIT)) {
                        whead = node;
                        node.prev = null;
                        return ns;
                    }
                }
                // 随机立减自旋次数,当自旋次数减为0时跳出循环再重试
                else if (LockSupport.nextSecondarySeed() >= 0 &&
                         --k <= 0)
                    break;
            }
        }
        //头节点为空?
        else if (h != null) { // help release stale waiters
            WNode c; Thread w;
            // 如果头节点的cowait链表(栈)不为空,唤醒里面的所有节点
            while ((c = h.cowait) != null) {
                if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                    (w = c.thread) != null)
                    U.unpark(w);
            }
        }
        
        // 如果头节点没有变化
        if (whead == h) {
            // 如果尾节点有变化,则更新
            if ((np = node.prev) != p) {
                if (np != null)
                    (p = np).next = node;   // stale
            }
            else if ((ps = p.status) == 0)
                // 如果尾节点状态为0,则更新成WAITING
                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);
                    
                //设置线程blocker    
                Thread wt = Thread.currentThread();
                U.putObject(wt, PARKBLOCKER, this);
                node.thread = wt;
                //1、当前节点前驱节点状态为WAITING;
                //2、当前节点的前驱节点不是头节点或有读写锁已经被获取;
                //3、头结点为改变;
                //4、当前节点的前驱节点未改变;
                //当以上4个条件都满足时将当前节点进行阻塞
                if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
                    whead == h && node.prev == p)
                    U.park(false, time);  // emulate LockSupport.park
                node.thread = null;
                //设置线程blocker为空
                U.putObject(wt, PARKBLOCKER, null);
                //当前节点百中断则取消节点
                if (interruptible && Thread.interrupted())
                    return cancelWaiter(node, node, true);
            }
        }
    }
}

写锁释放:

public void unlockWrite(long stamp) {
    WNode h;
    //因为写锁是独占锁,可以简单判断state != stamp;
    //或者bit7为0,即stamp状态为无写锁
    if (state != stamp || (stamp & WBIT) == 0L)
        throw new IllegalMonitorStateException();
    
    //修改state状态,state += WBIT;溢出则初始化为ORIGIN        
    state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
    //头节点不为空,且状态正常则释放头结点的锁
    if ((h = whead) != null && h.status != 0)
        release(h);
}
private void release(WNode h) {
    if (h != null) {
        WNode q; Thread w;
        //设置头节点状态为0
        U.compareAndSwapInt(h, WSTATUS, WAITING, 0);
        
        //头节点下个节点为空或状态为CANCEL,则从尾节点前向遍历,
        //找到头结点后面的第一个有效节点(状态为0或WAITTING)
        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.5、读锁获取及释放

读锁获取:

public long readLock() {
    long s = state, next;  // bypass acquireRead on common uncontended case
    //同步队列为空,读锁计数值未超过最大值(无写锁),CAS获取锁成功,则直接返回邮戳值;
    //否则acquireRead获取读锁
    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;;) {
                //(state & ABITS)小于RFULL,表示无写锁,则直接CAS获取读锁;
                //否则若(state & ABITS)小于WBIT表示无写锁但读锁计数已溢出,
                //则CAS更新读锁计数获取读锁
                if ((m = (s = state) & ABITS) < RFULL ?
                    U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                    (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
                    return ns;
                //(state & ABITS)大于WBIT,表示写锁已经被占,则自旋    
                else if (m >= WBIT) {
                    //随机减自旋次数
                    if (spins > 0) {
                        if (LockSupport.nextSecondarySeed() >= 0)
                            --spins;
                    }
                    else {
                        //自旋完还没获取到读锁?
                        if (spins == 0) {
                            WNode nh = whead, np = wtail;
                            //判断稳定性(有没有被修改),跳出循环
                            if ((nh == h && np == p) || (h = nh) != (p = np))
                                break;
                        }
                        //初始化spins
                        spins = SPINS;
                    }
                }
            }
        }
        //自旋获取失败,则进行节点初始化相关的处理
        //尾节点为空,则初始化队列
        if (p == 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(RMODE, p);
        //head==tail或者队列tail.mode不为读状态,
        //那么将当前线程的节点node加入到队列尾部并跳出外层循环    
        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;
            }
        }
        //如果head!= tail说明队列中已经有线程在等待或者tail.mode是读状态RMODE,
        //那么CAS方式将当前线程的节点node加入到tail节点的cowait链中
        else if (!U.compareAndSwapObject(p, WCOWAIT,
                                         node.cowait = p.cowait, node))
            node.cowait = null;
        else {
            for (;;) {
                WNode pp, c; Thread w;
                ////如果head不为空那么尝试去解放head的cowait链中的节点
                if ((h = whead) != null && (c = h.cowait) != null &&
                    U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) &&
                    (w = c.thread) != null) // help release
                    U.unpark(w);
                //如果tail节点的前驱就是head或者head==tail或者tail节点的前驱是null
                //也就是说当前node所在的节点(因为node可能在cowait链中)
                //的前驱就是head或者head已经被释放了为null    
                if (h == (pp = p.prev) || h == p || pp == null) {
                    long m, s, ns;
                    do {
                        //如果没有写状态被占有那么自旋方式尝试获取读状态,成功则返回stamp
                        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;
                    //如果tail的前驱是null或者head==tail或者tail已经被取消了(p.status > 0)
                    //直接将node置为null跳出循环,回到最开的for循环中去再次尝试获取同步状态
                    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;
                    
                    //tail的前驱不是head或者当前只有写线程获取到同步状态
                    //判断稳定性
                    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);
                }
            }
        }
    }

     //如果队列中没有节点或者tail的mode是WMODE写状态,
     //那么node被加入到队列的tail之后进入这个循环
    for (int spins = -1;;) {
        WNode h, np, pp; int ps;
        //如果p(node的前驱节点)就是head,那么自旋方式尝试获取同步状态
        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;
                //自旋方式尝试获取同步状态
                //获取成功的话将node设置为head并解放node的cowait链中的节点并返回stamp
                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;
                }
                //如果有写线程获取到了同步状态(因为可能有写线程闯入)那么随机的--k控制循环次数
                else if (m >= WBIT &&
                         LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
                    break;
            }
        }
        //如果head不为null,解放head的cowait链中的节点
        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);
            }
        }
         //判断稳定性
        if (whead == h) {
            if ((np = node.prev) != p) {
                if (np != null)
                    (p = np).next = node;   // stale
            }
            //尝试设tail的状态位WAITING表示后面还有等待的节点
            else if ((ps = p.status) == 0)
                U.compareAndSwapInt(p, WSTATUS, 0, WAITING);
            //如果tail已经取消了    
            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);
            }
        }
    }
}

读锁释放:

public void unlockRead(long stamp) {
    long s, m; WNode h;
    for (;;) {
        //写计数相关的bit位有改变,或读计数相关的bit位为0,或有写锁,则异常
        if (((s = state) & SBITS) != (stamp & SBITS) ||
            (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
            throw new IllegalMonitorStateException();
        //读锁计数未溢出?    
        if (m < RFULL) {
            if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                if (m == RUNIT && (h = whead) != null && h.status != 0)
                    release(h);
                break;
            }
        }
       //读锁计数溢出?    
        else if (tryDecReaderOverflow(s) != 0L)
            break;
    }
}

2.6、乐观锁获取

public long tryOptimisticRead() {
    long s;
    //当无写锁,则返回(state & SBITS),即高位的写计数;
    //否则返回0,即获取乐观锁失败
    return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
}

2.7、锁模式的转换

转换为写锁:将其他模式(如写锁、读锁、乐观锁)的锁转换为写锁

public long tryConvertToWriteLock(long stamp) {
    long a = stamp & ABITS, m, s, next;
    //写锁的状态未变?
    while (((s = state) & SBITS) == (stamp & SBITS)) {
        //当前无写/读锁?CAS获取写锁
        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;
        }
        //无写锁,有一个读锁,即当前线程为获取读锁的线程
        //直接cas将读锁释放并获取写锁,即state=(state - RUNIT + WBIT)
        else if (m == RUNIT && a != 0L) {
            if (U.compareAndSwapLong(this, STATE, s,
                                     next = s - RUNIT + WBIT))
                return next;
        }
        //当还有其他线程获取读锁时,自旋等待其他读锁释放
        else
            break;
    }
    return 0L;
}

主要处理:
当锁状态为无读/写锁时,通过CAS获取写锁;
当当前线程已获取写锁时,直接返回原本的邮戳;
当只有一个读锁,即当只有当前线程获取读锁,没有其他的读锁时,CAS释放读锁并获取写锁;
当获取读锁的线程数大于1,即除当前线程还有其他线程也获取到读锁时,自旋等待其他读锁释放;

转换为读锁:将读/写锁转换为读锁;

public long tryConvertToReadLock(long stamp) {
    long a = stamp & ABITS, m, s, next; WNode h;
    //无写锁?
    while (((s = state) & SBITS) == (stamp & SBITS)) {
        
        //无读/写锁,则CAS获取读锁
        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;
}

主要处理:
若无读/写锁,则CAS获取读锁;
若写锁为当前线程,则CAS释放写锁并获取读锁;
若当前线程已经获取读锁,则返回原本的邮戳;

转换为乐观锁:

public long tryConvertToOptimisticRead(long stamp) {
    long a = stamp & ABITS, m, s, next; WNode h;
    U.loadFence();
    for (;;) {
        //写锁状态变更?
        if (((s = state) & SBITS) != (stamp & SBITS))
            break;
        //无读/写锁?直接返回state    
        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;
        //读锁计数未溢出    
        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;
            }
        }
        //读锁计数溢出?
        else if ((next = tryDecReaderOverflow(s)) != 0L)
            return next & SBITS;
    }
    return 0L;
}

主要处理:
若当前线程已经获取写锁,则直接释放写锁;
若当前线程已经获取读锁,则CAS释放读锁;
当锁时,返回状态值state;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容