AQS源码阅读笔记(跟着子路老师的课)

子路老师的课

学习完子路老师的课之后自己分析的,可能存在问题。。。

AQS源码阅读

ReentrantLock里有内部得Sync类继承AQS,再有FairSync和NonFairSync继承Sync实现公平和非公平锁,下面阅读公平锁

FairSync有个lock方法

final void lock() {
    acquire(1);//这个1是AQS得status是1
}

然后咱们进入acquire方法,该方法再AQS里实现

public final void acquire(int arg) {
    if (!tryAcquire(arg) //尝试获取锁
        && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //加入等待队列
        selfInterrupt(); //可中断锁
}

可以看到这里是两个判断,咱们先看看 tryAcquire得实现,注意if里对该方法得返回值取反了,实现再FairSync里

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();//得到当前线程
    int c = getState(); //得到当前得status
    if (c == 0) { //如果当前status为0,则代表没人拿着锁,然后就CAS占有锁
        if (!hasQueuedPredecessors() //等会儿进去看看
            && compareAndSetState(0, acquires)) //这个就好说了CAS
        {
            setExclusiveOwnerThread(current); //设定为持有锁为当前线程,占有锁成功
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

我们刚才看到了hasQueuePredecessors(),在这里卡住,现在进入这个方法

public final boolean hasQueuedPredecessors() {
    Node t = tail; 
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

这里可以看到Node这个类,然后再看Node类,只看几个重点得

static final class Node {
	volatile int waitStatus; //代表当前线程得等待状态
    volatile Node prev; //前一个节点
    volatile Node next; //后一个节点
    volatile Thread thread;  //节点当前代表得线程
}

这个Node就是队列得节点类,实现是个deque,默认没有被初始化,

    private transient volatile Node head;
    private transient volatile Node tail;

借用子路老师名言,一切看源码,源码证明一切。

继续回到hasQueuePredecessors()

public final boolean hasQueuedPredecessors() {
    Node t = tail; 
    Node h = head;
    Node s;
    //这里在第一个节点过来得时候,head和tail都为空,此时 h == t,而return得是h!=t,
    //结果就为false,返回,然后往回看
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
    	//第一个等待线程不为空且当前线程是第一个等待线程才让它去获取
}

回到了tryAcquire方法

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();//得到当前线程
    int c = getState(); //得到当前得status
    if (c == 0) { //如果当前status为0,则代表没人拿着锁,然后就CAS占有锁
        if (!hasQueuedPredecessors() 
            //好了现在可以确定这里是false,取反就是true,然后执行下面得判断CAS
            && compareAndSetState(0, acquires)) //这个就好说了CAS
        {
            setExclusiveOwnerThread(current); //设定为持有锁为当前线程,占有锁成功
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

现在假设CAS成功,那么该方法返回为true,代表该线程占有锁成功,然后回到acquire方法

public final void acquire(int arg) {
    if (!tryAcquire(arg) 
        //刚才说到tryAcquire返回为true,但是if取!了,所以不判断后面直接完事,方法返回
        && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

返回回到Lock方法

final void lock() {
    acquire(1);
}

acquire执行完毕,lock返回,源码处继续向下执行。。。

终于最简单得情况分析完毕,下面回到刚才得分支出,也就是tryAcquire里c不为0,也就是锁被占用着得时候

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();//得到当前线程
    int c = getState(); //得到当前得status,刚才是0,现在是1
    if (c == 0) { 
        if (!hasQueuedPredecessors() 
            && compareAndSetState(0, acquires))
        {
            setExclusiveOwnerThread(current); 
            return true;
        }
    }
    //C为1进入这里,current是否为当前锁的持有者??
    //如果是,就进入,先假设是,也就是锁重入的过程分析
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        //nextc的意思是锁的引用计数+1,之后unlock要用到
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);//设定为新的引用技术
        return true;
        //返回true,这里不再赘述返回过程,和刚才返回true一样,代码继续向下进行
    }
    return false;
}

这就是ReentrantLock锁重入的过程分析,好咱们继续回到上一个分支,如果当前线程不是持有锁的线程

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();//得到当前线程
    int c = getState(); //得到当前得status,刚才是0,现在是1
    if (c == 0) { 
        if (!hasQueuedPredecessors() 
            && compareAndSetState(0, acquires))
        {
            setExclusiveOwnerThread(current); 
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    //没进if 也没进 else,直接返回false
    return false;
}

返回false后,我们返回到acquire方法

public final void acquire(int arg) {
    if (!tryAcquire(arg)  //tryAcquire为false,取反为true,然后进入下一个的判断
        &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

先看addWaiter(Node.EXCLUSIVE)方法

private Node addWaiter(Node mode) {
    //mode刚才传入的是Node.EXCLUSIVE此时为NULL,在Node节点的定义里可以看到
    Node node = new Node(Thread.currentThread(), mode); 
    //创建一个该线程的节点
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail; //这个节点的前驱是原来的尾节点
    if (pred != null) { //前驱不为空,则代表队列里有元素
        node.prev = pred; //连接该节点的prev到前驱节点
        if (compareAndSetTail(pred, node)) {//CAS方式设置新的tail
            pred.next = node;
            return node; //返回当前尾节点
        }
    }
    enq(node);
    return node;
}

现在是队列不为空的情况,下面是pred如果为null,那么将会进入enq方法,看看enq方法干了什么

private Node enq(final Node node) {
    for (;;) {//死循环
        Node t = tail; //当前的尾节点
        if (t == null) { // 如果为null,则代表需要初始化操作
            if (compareAndSetHead(new Node())) //cas创建头节点
                tail = head;//尾节点指向头节点
        } else { 
            //如果已经被初始化了,就是第一次循环过后尾节点就不空了,第二次循环就会进入到这里
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                //CAS把当前节点设定为尾节点,队列有个为null的空节点
                //代表着正拿着锁的那个线程,不理解之后再说
                t.next = node;
                return t;
            }
        }
    }
}

总之,addWaiter是返回当前新的尾节点,那我们在回到acquire

public final void acquire(int arg) {
    if (!tryAcquire(arg)  //tryAcquire为false,取反为true,然后进入下一个的判断
        &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

下面分析acquireQueued方法,把当前新的节点(里面是当前线程)作为参数传入

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true; //我也不懂,之后再说
    try {
        boolean interrupted = false; //这个之后锁中断回用到,现在用不到
        for (;;) {
            //死循环
            final Node p = node.predecessor(); 
            //找到当前节点的前驱节点,这里需要注意一下当前处于等待状态的第一个线程
            //第一个线程如果在等待,后面直接去睡觉不久行了,第一个都没拿到轮不到后面的
            
            //看下面这个if,p==head,现在假设当前线程就是第一个等待的线程,
            //刚才分析过 tryAcquire 的作用是通过CAS的方式竞争锁,这里的意思就是自旋一次,
            //假设这次自旋是拿到锁了,就进入了这个if
            if (p == head && tryAcquire(arg)) {
                setHead(node); //设定新头
                p.next = null; // 取消引用,让GC回收
                failed = false; // 
                return interrupted;//返回interrupted我也不知道为啥,再看看
            }
            //这次自旋没拿到锁,走到这里,这里需要预备park的前备知识
            //看看这个方法名 应该停下在获取失败后,也就很好理解了,先进去看一下
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

进去shouldParkAfterFailedAcquire(p, node)瞅瞅

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus; 
    //看看前一个节点的status,现在是第一个线程,pred也就是head节点
    //刚才头节点在初始化的时候,waitStatus没有赋值,默认为0
    if (ws == Node.SIGNAL) // Node.SIGNAL == -1
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //为0进入到这里,把前一个节点的waitStatus设为-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    //返回false
    return false;
}

waitStatus代表的是前一个thread的状态,每次新的线程过来的时候都会检查一下前一个的状态,如果前一个在睡,那它直接睡觉就行,这个方法返回了,那我们回到上一个方法。

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true; //我也不懂,之后再说
    try {
        boolean interrupted = false; //这个之后锁中断回用到,现在用不到
        for (;;) {
            //死循环
            final Node p = node.predecessor(); 
            //找到当前节点的前驱节点,这里需要注意一下当前处于等待状态的第一个线程
            //第一个线程如果在等待,后面直接去睡觉不久行了,第一个都没拿到轮不到后面的
            
            //看下面这个if,p==head,现在假设当前线程就是第一个等待的线程,
            //刚才分析过 tryAcquire 的作用是通过CAS的方式竞争锁,这里的意思就是自旋一次,
            //假设这次自旋是拿到锁了,就进入了这个if
            if (p == head && tryAcquire(arg)) {
                setHead(node); //设定新头
                p.next = null; // 取消引用,让GC回收
                failed = false; // 
                return interrupted;//返回interrupted我也不知道为啥,再看看
            }
            //这次自旋没拿到锁,走到这里,这里需要预备park的前备知识
            //看看这个方法名 应该停下在获取失败后,也就很好理解了,先进去看一下
            //现在shouldParkAfterFailedAcquire(p,node)返回的是false,此次循环结束
            //开启下一次循环
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
//下一次循环的情况
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true; //我也不懂,之后再说
    try {
        boolean interrupted = false; //这个之后锁中断回用到,现在用不到
        for (;;) {
            //死循环
            final Node p = node.predecessor(); 
            //这里如果是第一个等待节点,那它就再自旋1次,总计自旋两次
            if (p == head && tryAcquire(arg)) {
                setHead(node); //设定新头
                p.next = null; // 取消引用,让GC回收
                failed = false; // 
                return interrupted;//返回interrupted我也不知道为啥,再看看
            }
            //然后就又进入到了 shouldParkAfterFailedAcquire 方法
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

进入shouldParkAfterFailedAcquire

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus; 
    //看看前一个节点的status,现在是第一个线程,pred也就是head节点
    //刚才的时候把status设定为了-1,-1就是等待的状态
    if (ws == Node.SIGNAL) // Node.SIGNAL == -1
        //进入这里 返回true
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        //为0进入到这里,把前一个节点的waitStatus设为-1
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    //返回false
    return false;
}

返回到acquireQueued

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true; //我也不懂,之后再说
    try {
        boolean interrupted = false; //这个之后锁中断回用到,现在用不到
        for (;;) {
            //死循环
            final Node p = node.predecessor(); 
            //这里如果是第一个等待节点,那它就再自旋1次,第一个总计自旋3次
            if (p == head && tryAcquire(arg)) {
                setHead(node); //设定新头
                p.next = null; // 取消引用,让GC回收
                failed = false; // 
                return interrupted;//返回interrupted我也不知道为啥,再看看
            }
            //shouldParkAfterFailedAcquire返回了true,进行第二个的判断
            //parkAndCheckInterrupt
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

进入parkAndCheckInterrupt

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this); //把当前线程park,此时应该再这里被阻塞掉了,该方法不返回
    return Thread.interrupted(); //返回当前线程是否处于interrupted状态
}

以上就是第一个等待节点的全过程,哈哈哈哈,终于分析到这儿了,这个线程现在被阻塞着在等待队列里头呆着

第二个节点的分析就会简单很多,自行分析

下面是解锁的过程

public void unlock() {
    sync.release(1);
}

进入release,在AQS里

if (tryRelease(arg)) {
    Node h = head;
    if (h != null && h.waitStatus != 0)
        unparkSuccessor(h);
    return true;
}
return false;

先tryRelease了,进入tryRelease,这个在Sync类哈,注意Sync类里!!!

protected final boolean tryRelease(int releases) {
    int c = getState() - releases; 
    //当前status与应当的1作比较,如果>0则代表有重入,不可以释放掉
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        //是0,可以释放
        free = true;
        setExclusiveOwnerThread(null);//释放锁
    }
    setState(c);
    return free;
}

这样就可以理解了tryRelease如果发生了锁重入,则不会完全释放,如果没有锁重入,就会返回true

就可以进行锁的释放

if (tryRelease(arg)) {
    //tryRelease为true的情况
    Node h = head;
    if (h != null && h.waitStatus != 0) 
        //如果当前不为空,并且waitStatus不为0,为0的话就代表了后面没有新进来等待的线程
        unparkSuccessor(h);//进入unparkSuccessor
    return true;
}
return false;

进入unparkSuccessor

private void unparkSuccessor(Node node) {
    int ws = node.waitStatus; //当前节点的等待状态
    if (ws < 0) //如果当前节点的状态<0,那么久进行CAS把当前节点的waitStatus置为0
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next; //获取第一个等待线程,如果有等就是第二个,不是第二个的可能性不大
    if (s == null || s.waitStatus > 0) { //若为空,或者等待状态 > 0
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            //从后往前找找到第一个小于0的
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        //解锁这个线程
        LockSupport.unpark(s.thread);
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值