并发包源码解读——StampedLock

并发包源码解读——StampedLock


我们还是以类似这样的简单例子开头

public static void main(String[] args) throws InterruptedException {
        final StampedLock stampedLock = new StampedLock();
        final long stamp = stampedLock.writeLock();
        Thread.sleep(1000);
        new Thread(() ->{
            stampedLock.writeLock();
        }).start();
        stampedLock.unlockRead(stamp);
    }

1、构造器

state表示锁的状态,ORIGIN是一个初始值,同样还有好多和他们类似的初始值,可以看到他们都是经过了一系列位运算算出来的,可以算一下ORIGIN的值是256,为了方便观察我们记为1,00000000

public StampedLock() {
        state = ORIGIN;
    }
    private static final int LG_READERS = 7;
    //long是8字节也就是64位,前面若干0都省略
    //00000001
    private static final long RUNIT = 1L;
    //10000000
    private static final long WBIT  = 1L << LG_READERS;
    //01111111
    private static final long RBITS = WBIT - 1L;
    //01111110
    private static final long RFULL = RBITS - 1L;
    //11111111
    private static final long ABITS = RBITS | WBIT;
    //高57位为1,低7位为0
    private static final long SBITS = ~RBITS; 
    //1,00000000
    private static final long ORIGIN = WBIT << 1;

2、写锁

state低8位表示获取锁的状态,其中第8位表示是否加了写锁,低7位表示加读锁的次数

当我们取state低8位结果为0时,说明没人持有锁,那就放心地用cas改变持锁状态,state值为384也就是110000000

public long writeLock() {
        long s, next;  
        return ((((s = state) & ABITS) == 0L &&
                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
                next : acquireWrite(false, 0L));
    }

像STATE这种常量,是在调用构造器的时候初始化的

// Unsafe mechanics
    private static final sun.misc.Unsafe U;
    private static final long STATE;
    private static final long WHEAD;
    private static final long WTAIL;
    private static final long WNEXT;
    private static final long WSTATUS;
    private static final long WCOWAIT;
    private static final long PARKBLOCKER;

    static {
        try {
            U = sun.misc.Unsafe.getUnsafe();
            Class<?> k = StampedLock.class;
            Class<?> wk = WNode.class;
          	//见名知意,这个objectFieldOffset是根据对象属性的偏移量取值,前三个是属于锁的,中间3个是属于当前节点的
            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);
        }
    }

如果cas持锁失败,那就进入acquireWrite方法

private long acquireWrite(boolean interruptible, long deadline) {
        WNode node = null, p;
        for (int spins = -1;;) { // spin while enqueuing
            long m, s, ns;
            //前三个if、else if、else if是明显的自旋锁,最大自旋次数为SPINS的值64次,但实际上可能会比64次多,
            //因为LockSupport.nextSecondarySeed()生成随机数>=0才减
            if ((m = (s = state) & ABITS) == 0L) {
                if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT))
                    return ns;
            }
            else if (spins < 0)
                spins = (m == WBIT && wtail == whead) ? SPINS : 0;
            else if (spins > 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;;) {
            WNode h, np, pp; int ps;
            //第一波自旋时尾节点已经赋值给了p,其实p就是当前节点
            //如果头节点就是自己,就再自旋获取HEAD_SPINS也就是1024次,取锁失败了就扩大一倍尝试次数再试,最大能到MAX_HEAD_SPINS也就是65535次,除非线程抢到锁return,否则无法跳出自旋(其实就是线程阻塞了不断尝试获取锁)
            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;
                        }
                    }
                  //说明本轮cas次数已达上限,跳出内自旋进行后续逻辑
                    else if (LockSupport.nextSecondarySeed() >= 0 &&
                             --k <= 0)
                        break;
                }
            }
            //如果头节点不是自己
            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);
                }
            }
            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.thread = wt;
                    //该节点处于等待状态、或者队列中有线程获取锁,并且队列首尾都没变,就调用park阻塞0秒(其实就是不阻塞)
                    if (p.status < 0 && (p != h || (state & ABITS) != 0L) &&
                        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);
                }
            }
        }
    }

3、读锁

接下来我们试试readLock

加读锁调用readLock,如果有线程加读锁到上限,就成功获取锁

public long readLock() {
        long s = state, next;  // bypass acquireRead on common uncontended case
        return ((whead == wtail && (s & ABITS) < RFULL &&
                 U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
                next : acquireRead(false, 0L));
    }

失败就进入acquireRead尝试获取读锁

其实套路跟写锁一样,都是先尝试自旋尝试获取锁

private long acquireRead(boolean interruptible, long deadline) {
        WNode node = null, p;
        for (int spins = -1;;) {
            WNode h;
          	//队列中首尾节点相同,说明前面就一个节点,快轮到自己了,就自旋用cas获取锁
            if ((h = whead) == (p = wtail)) {
                for (long m, s, ns;;) {
                    if ((m = (s = state) & ABITS) < RFULL ?
                        U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
                        (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
                        return ns;
                    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;
                        }
                    }
                }
            }
            //尾节点是空,就初始化一个写的头节点,因为能阻塞获取读锁的就只有写锁(也就是初始化一个独占模式的头节点)
            //首尾节点初始化为同一个节点
            if (p == null) { // initialize queue
                WNode hd = new WNode(WMODE, null);
                if (U.compareAndSwapObject(this, WHEAD, null, hd))
                    wtail = hd;
            }
            //当前节点node为空,就初始化一个当前节点,把当前节点的前驱指向尾节点,作为入队后新的尾节点
            else if (node == null)
                node = new WNode(RMODE, p);
            //如果首尾节点相同,或者尾节点加的写锁(或者说不是共享模式的节点,是独占模式的节点)
            else if (h == p || p.mode != RMODE) {
                //当前节点的前驱不是尾节点就重置一下
                if (node.prev != p)
                    node.prev = p;
                //当前节点的前驱是尾节点,就用cas把尾节点更新为当前节点
                else if (U.compareAndSwapObject(this, WTAIL, p, node)) {
                    p.next = node;
                  	//成功进队之后,第一次自旋就结束了
                    break;
                }
            }
            //如果尾节点加的读锁(是共享模式的节点),就把尾节点的cowait更新为当前节点,失败的话就重置
          	//(我们可以发现,每个节点都有cowait,而cowait也是节点,所以这个cowait是一条链,里面是读节点相连,而这条‘读链’是在‘读链的头节点’作为阻塞队列的尾节点时被添加进去的)
            else if (!U.compareAndSwapObject(p, WCOWAIT,
                                             node.cowait = p.cowait, node))
                //cas失败了,说明有别的读锁先于当前线程更新了尾节点的cowait,那当前线程的cowait就重置
                node.cowait = null;
            //如果cas更新当前节点的cowait成功了
            else {
                for (;;) {
                    WNode pp, c; Thread w;
                    //如果队列中有头节点,且头节点有cowait,就移除头节点下的读链表中第一个读节点然后唤醒它
                    //并且唤醒头节点的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);
                    //如果首尾节点挨着或者相同,或者尾节点前驱是空的
                    if (h == (pp = p.prev) || h == p || pp == null) {
                        long m, s, ns;
                        //总的一句话就是自旋cas获取读锁
                        do {
                            //如果现在没写锁了,或者加读锁次数没到上限,就cas获取读锁
                            //如果加读锁次数也到上限了,就尝试获取读锁并记一次溢出
                            //获取成功了就return
                            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;
                        //代码执行到这已经没人占着锁了,就break出去
                        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);
                        //这里基本上就不会进来,因为interruptible显式为false
                        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;
                  	//加读锁次数没到上限,就cas改状态获取读锁,成功了就把自己变成头节点并移除前驱
                    //cas失败了就尝试获取读锁然后记读溢出
                    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;
                      	//如果当前节点有cowait,就依次唤醒cowait
                        while ((c = node.cowait) != null) {
                            if (U.compareAndSwapObject(node, WCOWAIT,
                                                       c, c.cowait) &&
                                (w = c.thread) != null)
                                U.unpark(w);
                        }
                        return ns;
                    }
                  	//如果有加写锁的痕迹,并且自旋次数达到上限,就随机break出去
                    else if (m >= WBIT &&
                             LockSupport.nextSecondarySeed() >= 0 && --k <= 0)
                        break;
                }
            }
          	//队列中有多个节点
            else if (h != null) {
                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
                }
              	//如果尾节点状态是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);
                    //这里基本上就不会进来,因为interruptible显式为false
                    if (interruptible && Thread.interrupted())
                        return cancelWaiter(node, node, true);
                }
            }
        }
    }

自旋中有个tryIncReaderOverflow方法,为了在加读锁达到上限之时,让之前已经进入自旋或阻塞的读锁后续能获取锁

private long tryIncReaderOverflow(long s) {
    // assert (s & ABITS) >= RFULL;
    //如果加读锁的次数达到上限,就重置回RBITS,然后记一次读溢出
    if ((s & ABITS) == RFULL) {
        if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
            ++readerOverflow;
            state = s;
            return s;
        }
    }
    //如果随机到了true,就给其他线程执行
    else if ((LockSupport.nextSecondarySeed() &
              OVERFLOW_YIELD_RATE) == 0)
        Thread.yield();
    //没到上限就返回0
    return 0L;
}

当获取锁超时了,调用cancelWaiter

private long cancelWaiter(WNode node, WNode group, boolean interrupted) {
    //group是传进来的尾节点
    if (node != null && group != null) {
        Thread w;
        node.status = CANCELLED;
        //循环移除尾节点的读链表中取消状态的节点
        for (WNode p = group, q; (q = p.cowait) != null;) {
            if (q.status == CANCELLED) {
                U.compareAndSwapObject(p, WCOWAIT, q, q.cowait);
                p = group; // restart
            }
            //否则就在下一次循环时使用当前节点的cowait判断状态,直到最后节点没有cowait
            else
                p = q;
        }
        //如果最后一个节点下的读链表中,第一个节点就是当前节点
        if (group == node) {
            //唤醒尾节点读链表中所有的节点对应的线程,让他们重新去竞争排队
            for (WNode r = group.cowait; r != null; r = r.cowait) {
                if ((w = r.thread) != null)
                    U.unpark(w);       // wake up uncancelled co-waiters
            }
            for (WNode pred = node.prev; pred != null; ) { // unsplice
                WNode succ, pp;        // find valid successor
              	//如果当前节点没后继或者后继是取消状态
                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;     // don't link if succ cancelled
                  	//如果循环到当前节点的后继了,或者这次循环的节点cas放到当前节点的后继了
                    if (succ == q ||   // ensure accurate successor
                        U.compareAndSwapObject(node, WNEXT,
                                               succ, succ = q)) {
                      	//如果当前节点的后继为空,并且当前节点就是尾节点,就cas把尾节点改成当前节点的前驱
                        //这个时候当前节点才与阻塞队列的联系断开
                        if (succ == null && node == wtail)
                            U.compareAndSwapObject(this, WTAIL, node, pred);
                        break;
                    }
                }
              	//如果没有新节点插进来的话,就把它的前驱指向它(之前是它指向它的前驱,单向关联,现在就是双向关联)
                if (pred.next == node) // unsplice pred link
                    U.compareAndSwapObject(pred, WNEXT, node, succ);
              	//这个节点取消了,就唤醒下一个节点
                if (succ != null && (w = succ.thread) != null) {
                    succ.thread = null;
                    U.unpark(w);       // wake up succ to observe new pred
                }
              	//如果当前节点的前驱的状态不是取消,或者前驱的前驱为空就跳出循环
                if (pred.status != CANCELLED || (pp = pred.prev) == null)
                    break;
                node.prev = pp;        // repeat if new pred wrong/cancelled
              	//succ是while循环中当前待取消节点的后继,pred是for循环中当前节点的前驱
                //那作用就很明显了,把当前节点的前驱和后继连起来,把自己替出去
                U.compareAndSwapObject(pp, WNEXT, pred, succ);
                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) {
          	//如果头节点的后继是空,或者后继是取消状态,就从后往前找状态<0的第一个节点
            for (WNode t = wtail; t != null && t != h; t = t.prev)
                if (t.status <= 0)
                    q = t;
        }
        if (h == whead) {
          	//之前在cas获取锁的时候如果如果失败了,会放到尾节点并标记status为等待状态
          	//当头节点的状态是0了说明轮到头节点获取锁了,那就得移除头节点
            if (q != null && h.status == 0 &&
                ((s = state) & ABITS) != WBIT && // waiter is eligible
                (s == 0L || q.mode == RMODE))
                release(h);
            break;
        }
    }
    return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L;
}

最后一步是调用release

private void release(WNode h) {
        if (h != null) {
            WNode q; Thread w;
            U.compareAndSwapInt(h, WSTATUS, WAITING, 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);
        }
    }

4、乐观读锁

本人不才写了一段丑陋易懂的代码

首先说一下怎么用

1、调用tryOptimisticRead方法返回一个戳stamp

2、本地赋值(读出来)

3、调用validate方法校验stamp,返回在读期间写锁版本号是否改变

4、改变的话升级为悲观读,重新赋值,然后释放悲观读锁

package com.company;

import java.util.concurrent.locks.StampedLock;

public class Main {

    private static int i;

    public static void main(String[] args) {
        final StampedLock stampedLock = new StampedLock();
        new Thread(new ReadThread(stampedLock)).start();
        new Thread(new WriteThread(stampedLock)).start();
    }

    static class ReadThread implements Runnable{

        private final StampedLock stampedLock;

        public ReadThread(StampedLock stampedLock) {
            this.stampedLock = stampedLock;
        }

        @Override
        public void run() {
            long stamp = stampedLock.tryOptimisticRead();
            int icopy = i;
            System.out.println("第一次读"+icopy);
            if(!stampedLock.validate(stamp)){
                System.out.println("有写锁把值改了");
                stamp = stampedLock.readLock();
                icopy = i;
                stampedLock.unlockRead(stamp);
            }
            System.out.println("最后的读"+icopy);
        }
    }

    static class WriteThread implements Runnable{

        private final StampedLock stampedLock;

        public WriteThread(StampedLock stampedLock) {
            this.stampedLock = stampedLock;
        }

        @Override
        public void run() {
            long stamp = stampedLock.writeLock();
            System.out.println("加写锁");
            i++;
            System.out.println(i);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("释放写锁");
            stampedLock.unlockWrite(stamp);
        }
    }
}

接下来再讲原理

调用tryOptimisticRead方法,取状态的高57位作为stamp返回

public long tryOptimisticRead() {
        long s;
        return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
    }

可以发现乐观读只使用了位运算,并没有真正的加锁。刚才我们说了,高57位表示读锁的版本,只要我们在加乐观读锁之后判断写锁的版本号有没有改变(在读期间有没有加过写锁,数据有没有改变),就可以保证数据的可靠性

校验的方法是validate

首先调用了unsafe的loadFence方法,见名知意我们翻译为读屏障,用来保证loadFence之前的所有读操作都完成

这个读操作是指把主存中的数据都刷进线程缓存,保证内存一致性

public boolean validate(long stamp) {
    U.loadFence();
    return (stamp & SBITS) == (state & SBITS);
}

实际上我还是没太懂为什么要调用loadFence,猜测是为了把stamp刷进缓存

5、释放锁

直接调用unlock方法,传入当前线程加锁时生成的stamp

public void unlock(long stamp) {
        long a = stamp & ABITS, m, s; WNode h;
        while (((s = state) & SBITS) == (stamp & SBITS)) {
            //都已经没锁了就直接break
            if ((m = s & ABITS) == 0L)
                break;
            //如果加的写锁(m刚才state给赋值了,是加锁状态)
            else if (m == WBIT) {
                //释放锁用的标志对不上(跟加锁返回的stamp不同),就break抛异常
                if (a != m)
                    break;
                //这里不用cas减WBIT反而要加,这样不仅不会影响写位的判断,还能通过每次写操作给锁赋予‘版本更新’的特性
              	//但版本也是有上限的,long一共64位,低7位表示读次数,高57位区分写版本号,溢出之后重置为ORIGIN
                state = (s += WBIT) == 0L ? ORIGIN : s;
                //等待队列中有头节点且下一个节点状态不是初始状态,就移除头节点
                if ((h = whead) != null && h.status != 0)
                    release(h);
                return;
            }
            //没加读锁,状态不对就break抛异常(释放锁的时候不可能没锁或者读写锁同时存在)
            else if (a == 0L || a >= WBIT)
                break;
            //如果加读锁次数小于加读锁的次数上限,这种状态是正常的
            else if (m < RFULL) {
              	//cas尝试释放一个读锁
                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();
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wheat_Liu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值