AQS源码学习

21 篇文章 1 订阅
5 篇文章 0 订阅

AQS,全称:AbstractQueuedSynchronizer,是JDK提供的一个同步框架,内部维护着FIFO双向队列,即CLH同步队列。

        AQS依赖它来完成同步状态的管理(voliate修饰的state,用于标志是否持有锁)。如果获取同步状态state失败时,会将当前线程及等待信息等构建成一个Node,将Node放到FIFO队列里,同时阻塞当前线程,当线程将同步状态state释放时,会把FIFO队列中的首节的唤醒,使其获取同步状态state。

很多JUC包下的锁都是基于AQS实现的

节点代码:

static final class Node {
        /** 共享节点 */
        static final Node SHARED = new Node();

        /** 独占节点 */
        static final Node EXCLUSIVE = null;

        /** 因为超时或者中断,节点会被设置成取消状态,被取消的节点不会参与到竞争中,会一直是取消
            状态不会改变 */
        static final int CANCELLED =  1;

        /** 后继节点处于等待状态,如果当前节点释放了同步状态或者被取消,会通知后继节点,使其得以
            运行 */
        static final int SIGNAL = -1;

        /** 节点在等待条件队列中,节点线程等待在condition上,当其他线程对condition调用了signal
            后,该节点将会从等待队列中进入同步队列中,获取同步状态 */
        static final int CONDITION = -2;

        /**
         * 下一次共享式同步状态获取会无条件的传播下去
         */
        static final int PROPAGATE = -3;

        /** 等待状态 */
        volatile int waitStatus;

        /** 前驱节点 */        
        volatile Node prev;

        /** 后继节点 */
        volatile Node next;

        /** 获取同步状态的线程 */
        volatile Thread thread;

        /**
         * 下一个条件队列等待节点
         */
        Node nextWaiter;

        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

独占式同步状态过程


/**
 * 独占式获取同步状态
 */
public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
        selfInterrupt();
    }
}

tryAcquire

尝试去获取锁,获取成功返回true,否则返回false。该方法由继承AQS的子类自己实现。采用了模板方法设计模式。

如:ReentrantLock的Sync内部类,Sync的子类:NonfairSync(非公平锁)和FairSync(公平锁)

FairSync:内部重写了tryAcquire方法,一下是未重写的

protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}

FairSync也是ReentrantLock的内部类,继承Sync

里面重写了两大方法,一个是重写了lock方法,另外一个是tryAcquire方法

    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

        /**
         * 公平版的tryAcquire,尝试获取锁
         */
        protected final boolean tryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            //获取锁的状态,state是AQS的属性,表示锁的同步状态
            int c = getState();
            //判断锁的状态,如果为0那么锁就是释放状态
            if (c == 0) {
                //若锁为释放状态,那么先判断是否处于队列头部
                //若处于队列头部那么CAS尝试获取锁
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    //若锁为释放状态,且通过cas争抢到了锁,那么将该线程赋值给AbstractOwnableSynchronizer的exclusiveOwnerThread属性
                    setExclusiveOwnerThread(current);
                    //返回争抢到了锁
                    return true;
                }
            }
            //如果锁处于未释放状态那么判断可重入性
            //若当前线程就是AbstractOwnableSynchronizer的exclusiveOwnerThread,那么标记重入
            else if (current == getExclusiveOwnerThread()) {
                //c就是当前state,acquires是传入的参数
                int nextc = c + acquires;
                //一般不可能加成0的,程序健壮性
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                //将state状态设置为原本的状态值加上获取次数
                setState(nextc);
                //返回争抢到了锁(重入锁)
                return true;
            }
            //若没有争抢到锁,同时也不是可重入锁的主人,那么就返回false表示没抢到
            return false;
        }
    }

AQS内部维护的acquire方法,在本片开头就有,目的是尝试获取锁,在尝试的过程中,第一步就是调用try Acquire方法看是否能够获取锁。

public final void acquire(int arg) {
    //第一步判断是否抢到了锁,若没抢到,开始第二步入队列
    //addWaiter方法就是添加队列的方法,其中node的Exclusive属性(独占节点)默认是null
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

addWaiter(第一次添加队列)

//mode值就是Node节点的final静态变量:EXCLUSIVE,默认是null,所以此处mode为null
private Node addWaiter(Node mode) {
        // 新建Node节点,该node的Thread值就是null,而不是没抢到锁的节点
        Node node = new Node(Thread.currentThread(), mode);
        // 设置一个pred,将Node的tail赋值给pred,尝试快速添加尾结点
        Node pred = tail;
    	// 第一次添加,AQS的tail尾部一定是null,所以此处头结点也是null
    	// 第一次添加,一定不成立,那么跳过if来到enq方法
        if (pred != null) {
            node.prev = pred;
            // CAS方式设置尾结点
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 如果上面添加失败,这里循环尝试添加,直到添加成功为止(通过自旋CAS进行添加操作)
    	// 注意此处的node节点中的线程就是目前线程的节点
        enq(node);
        return node;
    }

enq(CAS自旋添加)


    private Node enq(final Node node) {
        // 一直for循环,直到插入Node成功为止
        for (;;) {
            //拿到AQS指向的尾节点
            Node t = tail;
            if (t == null) {
                // CAS设置首节点,如果设置不成功,那么又会来到下一次循环进行设置,设置的过程CAS原子性
                // 注意,这里不是目前线程所在节点,而是new了一个新的空节点
                if (compareAndSetHead(new Node()))
                    //设置成功,AQS指向的头部变成了新的空节点,开始下一次循环
                    tail = head;
            } else {
                //最后一次循环,AQS的尾部已经有尾部节点了,那么来到else这一边
                //将尾部节点赋值给
                node.prev = t;
                // CAS设置尾结点
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
                //此后的尾节点有两种,一种的包含着线程的尾节点,还有一种是队中只有一个的空节点
            }
        }
    }

acquireQueued

方法返回到acquire,此时开始acquireQueued操作

    final boolean acquireQueued(final Node node, int arg) {
        // 操作是否成功标志
        boolean failed = true;
        try {
            // 线程中断标志
            boolean interrupted = false;
            // 不断的自旋循环
            for (;;) {
                // 当前节点的prev节点
                final Node p = node.predecessor();
                // 判断prev是否是头结点 && 是否获取到同步状态
                // 获取当前线程所在队列节点的前一个节点,看前一个节点是否是头结点(也就是空节点)
                //如果是空节点,那么该线程所在位置就是队列的第一,那么此时可以尝试获取锁tryAcquire
                if (p == head && tryAcquire(arg)) {
                    // 当前线程节点正好是队一,且抢到了锁
                    // 将当前节点设置成头结点
                    setHead(node);//此时空节点还在
                    // 将prev节点移除队列中
                    // 空节点无了,换成了当前线程的节点
                    p.next = null; // help GC垃圾回收
                    //获取成功
                    failed = false;
                    //此时线程没有中断,返回false,acquire方法返回,lock方法返回,当前线程获得锁
                    //此时的当前线程所在的节点扮演了原来空节点的角色
                    return interrupted;
                }
                // 不在队头,或在队头没抢到锁,那么开始自旋,如果自旋到一定次数(一次),那么阻塞
                // 自旋过程中,判断当前线程是否需要阻塞 && 阻塞当前线程并且检验线程中断状态
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())//自旋一次的目的是尽量争抢执行权,防止被park
                    interrupted = true;
            }
        } finally {
            if (failed)
                // 取消获取同步状态
                cancelAcquire(node);
        }
    }

shouldParkAfterFailedAcquire

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        // 拿到当前节点的prev节点的等待状态,如果没有改动,那么默认等待状态为0
        int ws = pred.waitStatus;
        
        //SIGNAL是标记量,标记当前节点是否释放了同步状态或者被取消
       	//如果当前线程第一次自旋,那么ws一般都是0
        if (ws == Node.SIGNAL)
            /*
             * 如果prev的status是signal,表示当prev释放了同步状态或者取消了,会通知当前节        
             * 点,所以当前节点可以安心的阻塞了(相当睡觉会有人叫醒他)
             */
            return true;
        if (ws > 0) {
            /*
             * status > 0,表示为取消状态,需要将取消状态的节点从队列中移除
             * 直到找到一个状态不是取消的节点为止
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * 除了以上情况,通过CAS将prev的status设置成signal
             */
            // 自旋一次结束,将前一个节点的状态设置为释放同步或者取消
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

parkAndCheckInterrupt

	private final boolean parkAndCheckInterrupt() {
        // 阻塞当前线程
        LockSupport.park(this);
        // 返回当前线程的中断状态
        return Thread.interrupted();
    }	

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Aristocrat l

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

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

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

打赏作者

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

抵扣说明:

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

余额充值