AbstractQueueSynchronsizer源码阅读(一)

aqs读了好久,一直磕磕绊绊,还是自己捋一遍记忆比较深刻。

Node数据结构

  • waitstatus
waiteStatus 含义
0 当一个Node被初始化的时候的默认值
CANCELLED 为1,表示线程获取锁的请求已经取消了
CONDITION 为-2,表示节点在条件队列中,节点线程等待唤醒
PROPAGATE 为-3,当前线程处在SHARED情况下,该字段才会使用
SIGNAL 为-1,表示线程已经准备好了,就等资源释放了

volatile status

我们可以通过修改State字段表示的同步状态来实现多线程的独占模式和共享模式(加锁过程)

独占模式.png
共享模式
  • prev、next

这里主要在阻塞队列中使用(阻塞队列是一个双向链表)

  • nextWaiter

在条件队列中使用(单向链表)

加锁过程

这里是通过ReentrantLock的非公平锁来分析加锁过程的。

且jdk版本为openJdk11。

//java.util.concurrent.locks.ReentrantLock
public void lock() {
    //这里调用的是aqs抽象类的模板方法
    this.sync.acquire(1);
}

//java.util.concurrent.locks.AbstractQueuedSynchronizer
public final void acquire(int arg) {
    //这里的主要逻辑,当尝试获取锁不成功的话,会将当前线程加入阻塞队列中
    if (!this.tryAcquire(arg) && this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg)) {
        selfInterrupt();
    }
}

注:这个模板方法要记好,因为整个加锁流程是按这个大方向走的。

这里就先按非公平锁的过程走一遍。

//java.util.concurrent.locks.ReentrantLock
protected final boolean tryAcquire(int acquires) {
    return this.nonfairTryAcquire(acquires);
}
//java.util.concurrent.locks.ReentrantLock.NonfairSync
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
    Thread current = Thread.currentThread();//获取当前线程
    int c = this.getState();//获取当前state值
    //如果state值为0,说明当前没有线程申请锁,这样作为非公平锁就可以
    //直接申请锁,不用管阻塞队列是否有线程等待。
    if (c == 0) {
        //通过cas获取锁(即将state修改为acquires)
        if (this.compareAndSetState(0, acquires)) {
            //获取锁成功后,可以将exclusiveOwnerThread设置为当前线程
            //这将为可重入锁提供判断依据
            this.setExclusiveOwnerThread(current);
            return true;
        }
        //这里就是判断获取锁的线程和当前申请锁的线程是否为同一个线程,实现锁的可重入
    } else if (current == this.getExclusiveOwnerThread()) {
        //重入一次,state值加一
        int nextc = c + acquires;
        if (nextc < 0) {
            throw new Error("Maximum lock count exceeded");
        }
                    
        this.setState(nextc);
        return true;
    }

    return false;
}

不行,我感觉还要贴一下那个模板方法:

//java.util.concurrent.locks.AbstractQueuedSynchronizer
public final void acquire(int arg) {
    //这里的主要逻辑,当尝试获取锁不成功的话,会将当前线程加入阻塞队列中
    if (!this.tryAcquire(arg) && this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg)) {
        selfInterrupt();
    }
}

如果tryAcquire(arg)获取锁成功,那acquire方法结束,加锁失败才是最复杂的。

这里我们先看addWaiter方法。

//java.util.concurrent.locks.AbstractQueuedSynchronizer
private AbstractQueuedSynchronizer.Node addWaiter(AbstractQueuedSynchronizer.Node mode) {
    AbstractQueuedSynchronizer.Node node = new AbstractQueuedSynchronizer.Node(mode);

    AbstractQueuedSynchronizer.Node oldTail;
    do {
        while(true) {
            //获取旧的尾节点
            oldTail = this.tail;
            //如果尾节点不为null,将该node设置为尾节点
            if (oldTail != null) {
                //这条语句我猜测是node.prev = oldTail的意思,如果理解错误,还请指出
                node.setPrevRelaxed(oldTail);
                //跳出内层while循环
                break;
            }
            //如果为空,就先初始化阻塞队列,就是创造一个空的头结点,
            //使头结点和尾节点同时指向这个空节点(这里的空节点不是null节点,
            //不要理解错误,有兴趣可以看看这个方法,没有几行)
            this.initializeSyncQueue();
        }
        //使用cas将tail设置为node节点,设置成功跳出外层循环
    } while(!this.compareAndSetTail(oldTail, node));
         //最后将旧的尾节点的next指向新的尾节点,这里也有一个注意点,下一篇会讲到
    oldTail.next = node;
    //将node节点返回(也就是将尾节点返回给acquireQueued方法)
    return node;
}

这里画了个图,帮助理解一下这个过程:


addWaiter
    final boolean acquireQueued(AbstractQueuedSynchronizer.Node node, int arg) {
        boolean interrupted = false;

        try {
            while(true) {
                //获取node的前驱节点
                AbstractQueuedSynchronizer.Node p = node.predecessor();
                //如果node的前驱节点为head,并且尝试获取锁成功,则将当前node设置为head
                if (p == this.head && this.tryAcquire(arg)) {
                    this.setHead(node);
                    p.next = null;
                    //唯一出口,当挂起的线程唤醒后,后继续执行while循环,当满足if条件的时候,会返回。
                    return interrupted;
                }
                //如果不满足上面的if语句,则会执行下面这段代码主要做了以下几件事(本来想直接在分析
                //shouldParkAfterFailedAcquire方法时再说,但是怕大家连贯不起来):
                //1.前驱节点的waitstatus==1直接返回true
                //2.剔除掉已经取消的节点
                //3.将node的前驱节点赋值为-1,返回false,这时这里的while循环会在执行一次,就会变成1的情况
                if (shouldParkAfterFailedAcquire(p, node)) {
                    //将当前线程挂起,并记录中断状态,以便返回给上层调用函数
                    interrupted |= this.parkAndCheckInterrupt();
                }
            }
        } catch (Throwable var5) {
            //这里我看了一下,貌似只有两处会抛出异常
            //1.node.predecessor();会抛出空指针异常
            //2.this.tryAcquire(arg);会抛一个Error
            this.cancelAcquire(node);
            if (interrupted) {
                selfInterrupt();
            }
            throw var5;
        }
    }

shouldParkAfterFailedAcquire方法在上面已经介绍的差不多了,为了完整,我再贴一遍。

private static boolean shouldParkAfterFailedAcquire(AbstractQueuedSynchronizer.Node pred, AbstractQueuedSynchronizer.Node node) {
    //获取前驱节点的waitStatus值
    int ws = pred.waitStatus;
    //如果等于-1就直接返回true
    if (ws == -1) {
        return true;
    } else {
        //否则,判断前驱节点是否取消(CANCEL值为1)了
        if (ws > 0) {
            //如果前驱节点取消了,就往前找,直到找到一个ws <= 0的
            do {
                node.prev = pred = pred.prev;
            } while(pred.waitStatus > 0);

            pred.next = node;
        } else {
            //将前驱节点的waitstatus值设置为-1
            pred.compareAndSetWaitStatus(ws, -1);
        }
        return false;
    }
}

在这里我想多说一句,看这一个方法的时候,也要把控一下全局,有时候可能一时理解不了,因为没有结合上下文来看,思路很有可能就会被堵住,我就是这样:sob:。

继续继续,如果shouldParkAfterFailedAcquire返回true,就该执行parkAndCheckInterrupt方法了。

private final boolean parkAndCheckInterrupt() {
    //将当前线程挂起(注意,中断也可以将park掉的线程给唤醒)
    LockSupport.park(this);
    //将中断状态返回
    return Thread.interrupted();
}

再回来看这个模板方法:

public final void acquire(int arg) {
    if (!this.tryAcquire(arg) && this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg)) {
        //如果acquireQueued返回true的话,代表加锁过程有中断,需要将中断状态延续下去
        selfInterrupt();
    }
}

参考文章:

https://www.javadoop.com/post/AbstractQueuedSynchronizer

https://tech.meituan.com/2019/12/05/aqs-theory-and-apply.html

你可能感兴趣的:(AbstractQueueSynchronsizer源码阅读(一))