(十三)JUC - AQS(AbstractQueuedSynchronizer,抽象的队列同步器)

  • AQS重要性:

Java ---> JVM

JUC  ---> AQS

主要用于解决锁分配给谁的问题。

整体就是一个抽象的FIFO队列来完成资源获取线程的排队工作,并通过一个int类变量表示持有锁的状态。

和AQS有关的

进一步理解锁和同步器的关系

,面向锁的使用者

同步器,面向锁的实现者

AQS源码说明

int 默认值0

AQS同步队列的基本结构

AQS内部体系架构

“银行候客区的顾客”

1. AQS源码分析

ReentrantLock,从最简单的lock方法看公平和非公平

公平锁 vs 非公平锁

公平锁 vs 非公平锁 ----- 唯一的区别

hasQueuedPredecessors判断了是否需要排队

非公平锁:“不讲武德”,一来就抢

acquire()  ---- 用了 模板方法设计模式

尝试获取锁,获取不到,排队(阻塞当前线程)

1)tryAcquire()

tryAcquire 方法,尝试获取锁。以下几种情况,会导致获取锁失败:

  • 锁已经被其他线程获取;
  • 锁没有被其他线程获取,但当前线程需要排队;
  • cas失败(可能过程中已经有其他线程拿到锁了)

锁为自由状态(c == 0),并不能说明可以立即执行cas获取锁,因为可能在当前线程获取锁之前,已经有其他线程在排队了,必须遵循先来后到原则获取锁。所以还要调用hasQueuedProdecessors方法,查看自己是否需要排队。

     /**

         * Fair version of tryAcquire.  Don't grant access unless

         * recursive call or no waiters or is first.

         */

        protected final boolean tryAcquire(int acquires) {

            final Thread current = Thread.currentThread();

            int c = getState();

            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;

            }

            return false;

        }

2)addWaiter()

尝试获取锁失败的情况下,将当前线程封装成Node对象,并加入到排队队列中。

enq()

没有其他线程在排队,调用enq构造队列,并将node加入到队列。

waitStatus

顾客A在办业务;

顾客B去排队:

head, tail

  • new Node() 哨兵节点,虚拟节点,占位  Thread = null, waitStatus = 0
  • 节点B,Thread = 顾客B,waitStatus = 0

3)acquireQueued()

整个aqs的核心和难点之一。

注意这里使用了for(;;)

判断node的前辈节点是不是head

shouldParkAfterFailedAcquire

parkAndCheckInterrupt()

用的LockSupport.park()

当前节点把前驱节点的waitStatus更新为-1

4)unlock()  --> sync.release(1)   -->  tryRelease()  -->  unparkSuccessor()

release()

tryRelease()

unparkSuccessor()

unpark后,acquireQueued大循环还没完呢。。。

5)cancelAcquire()

异常情况 if(failed)

  /**

     * Cancels an ongoing attempt to acquire.

     *

     * @param node the node

     */

    private void cancelAcquire(Node node) {

        // Ignore if node doesn't exist

        if (node == null)

            return;



        node.thread = null;



        // Skip cancelled predecessors

        Node pred = node.prev;

        while (pred.waitStatus > 0)

            node.prev = pred = pred.prev;



        // predNext is the apparent node to unsplice. CASes below will

        // fail if not, in which case, we lost race vs another cancel

        // or signal, so no further action is necessary.

        Node predNext = pred.next;



        // Can use unconditional write instead of CAS here.

        // After this atomic step, other Nodes can skip past us.

        // Before, we are free of interference from other threads.

        node.waitStatus = Node.CANCELLED;



        // If we are the tail, remove ourselves.

        if (node == tail && compareAndSetTail(node, pred)) {

            compareAndSetNext(pred, predNext, null);

        } else {

            // If successor needs signal, try to set pred's next-link

            // so it will get one. Otherwise wake it up to propagate.

            int ws;

            if (pred != head &&

                ((ws = pred.waitStatus) == Node.SIGNAL ||

                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&

                pred.thread != null) {

                Node next = node.next;

                if (next != null && next.waitStatus <= 0)

                    compareAndSetNext(pred, predNext, next);

            } else {

                unparkSuccessor(node);

            }



            node.next = node; // help GC

        }

    }

2. AQS 总结

整个ReentrantLock的加锁过程,可以分为三个阶段:

1)尝试加锁;

2)加锁失败,线程入队列;

3)线程入队列后,进入阻塞状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值