AQS框架

1、框架

AQS框架_第1张图片

其类结构扩展点为

AQS框架_第2张图片

有两种资料共享方式:Exclusive和Share

结点状态5种

CANCELED(1):表示结点已取消调度

SIGNAL(-1):表示后继结点在等待当前结点唤醒,后继结点入队时,会将前继结点状态更新为SIGNAL

CONDITION(-2):表示结点等待在Condition上,当其它线程调用Condition的signal方法后,CONDITION状态的结点将从等待队列转移到同步队列中。等待获取同步锁

PROPAGATE(-3):在共享模式下,前继结点不仅会唤醒其后继结点,同时也可能会唤醒后继的后继结点。

0:新结点入队进的默认状态

 

2、加锁流程

AQS框架_第3张图片

 

2.1 acquire(int)

独占模式下线程获取共享资料,如果获取到资源,直接返回,否则进入等待队列,直到获取到资料为止,同时会忽略中断标志

public final void acquire(int arg) {

        if (!tryAcquire(arg) &&

            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

            selfInterrupt();

    }

(1)tryAcquire尝试直接获取资源,如果成功直接返回

(2)addWaiter将该线程加入到队列尾部,并标记为独占模式

(3)acquireQueued使线程阻塞在等待队列中获取资料,一直获取到资料后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false

(4)如果线程在等待过程中被中断过,它是不忽略的。因为在获取到资源后用selfInterrupt清除了中断标志

 

2.1.1 addWaiter(Node)

将当前线程放入等待队列中尾部,并返回当前线程所在的结点

private Node addWaiter(Node mode) {

        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;

            if (compareAndSetTail(pred, node)) {

                pred.next = node;

                return node;

            }

        }

        enq(node);

        return node;

    }

2.1.2 acquireQueued(Node, int)

获取资源失败后,进入阻塞状态等待被唤醒

final boolean acquireQueued(final Node node, int arg) {

    boolean failed = true;//标记是否成功拿到资源

    try {

        boolean interrupted = false;//标记等待过程中是否被中断过

 

        //又是一个“自旋”!

        for (;;) {

            final Node p = node.predecessor();//拿到前驱

            //如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。

            if (p == head && tryAcquire(arg)) {

                setHead(node);//拿到资源后,将head指向该结点。所以head所指的标杆结点,就是当前获取到资源的那个结点或null。

                p.next = null; // setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!

                failed = false; // 成功获取资源

                return interrupted;//返回等待过程中是否被中断过

            }

 

            //如果自己可以休息了,就通过park()进入waiting状态,直到被unpark()。如果不可中断的情况下被中断了,那么会从park()中醒过来,发现拿不到资源,从而继续进入park()等待。

            if (shouldParkAfterFailedAcquire(p, node) &&

                parkAndCheckInterrupt())

                interrupted = true;//如果等待过程中被中断过,哪怕只有那么一次,就将interrupted标记为true

        }

    } finally {

        if (failed) // 如果等待过程中没有成功获取资源(如timeout,或者可中断的情况下被中断了),那么取消结点在队列中的等待。

            cancelAcquire(node);

    }

}

 

2.1.3 shouldParkAfterFailedAcquire(Node, Node)

主要检查状态是否可以进入waiting状态,如果前驱结点状态是SIGNAL,说明可以,如果是CANCELED,说明前驱结点已经被取消,需要继续向前找直到找到可用状态的结点,将当前结点放入其后。否则将前驱结点状态更新为SIGNAL

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {

        int ws = pred.waitStatus;

        if (ws == Node.SIGNAL)

            /*

             * This node has already set status asking a release

             * to signal it, so it can safely park.

             */

            return true;

        if (ws > 0) {

            /*

             * Predecessor was cancelled. Skip over predecessors and

             * indicate retry.

             */

            do {

                node.prev = pred = pred.prev;

            } while (pred.waitStatus > 0);

            pred.next = node;

        } else {

            /*

             * waitStatus must be 0 or PROPAGATE.  Indicate that we

             * need a signal, but don't park yet.  Caller will need to

             * retry to make sure it cannot acquire before parking.

             */

            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);

        }

        return false;

    }

 

2.1.4 parkAndCheckInterrupt

如果线程找好安全休息点后,就可以安心去休息了。此方法是让线程去休息,真正进入等待状态

private final boolean parkAndCheckInterrupt() {

        LockSupport.park(this);

        return Thread.interrupted();

    }

 

3、解锁流程

3.1 unparkSuccessor(Node)

该方法是唤醒等待队列中下一个线程

private void unparkSuccessor(Node node) {

        /*

         * If status is negative (i.e., possibly needing signal) try

         * to clear in anticipation of signalling.  It is OK if this

         * fails or if status is changed by waiting thread.

         */

        int ws = node.waitStatus;

        if (ws < 0)

            compareAndSetWaitStatus(node, ws, 0);

        /*

         * Thread to unpark is held in successor, which is normally

         * just the next node.  But if cancelled or apparently null,

         * traverse backwards from tail to find the actual

         * non-cancelled successor.

         */

        Node s = node.next;

        if (s == null || s.waitStatus > 0) {

            s = null;

            for (Node t = tail; t != null && t != node; t = t.prev)

                if (t.waitStatus <= 0)

                    s = t;

        }

        if (s != null)

            LockSupport.unpark(s.thread);

    }

你可能感兴趣的:(java)