AbstractQueuedLongSynchronizer框架源码分析

本文深入解析了AbstractQueuedSynchronizer(AQS)的工作原理,包括其核心数据结构CLH队列,volatile int state的三种访问方式,以及Node结构的详细解释。文章详细阐述了AQS在独占模式和共享模式下获取和释放同步状态的过程,以及线程中断和时间限制下的行为。

**

一、描述

AQS是AbstractQueuedLongSynchronizer依赖于先进先出FIFO队列阻塞和相关同步器的
(信号量,管程)提供的一个框架。AQS继承了AbstractOwnableSynchronizer类,这个类
为创建锁和相关的同步器提供了基础。AQS是Concurrent包的核心,lock就是阻塞队列来实现。

二、框架

在这里插入图片描述
它维护了volatile int state(代表共享资源)和一个FIFO线程等待队列,state的访问方式有三种:

  • getState()
  • setState(long newState)
  • compareAndSetState(long expect, long update)

CLH队列

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中唤醒
        static final int CONDITION = -2;
        //表示下一个acquireShared需要无条件的传播
        static final int PROPAGATE = -3;

        /**
          *   SIGNAL: 当前节点的后继节点处于等待状态时,如果当前节点的同步状态被释   
		 * 放或者取消,
		 *  CANCELLED:  一个节点由于超时或者中断需要在CLH队列中取消等待状态,被
		 * 取消的节点不会再次等待
		 *    CONDITION:  当前节点在等待队列中,只有当节点的状态设为0的时候该节
		 * 才会被转移到同步队列
		 * PROPAGATE:  下一次的共享模式同步状态的获取将会无条件的传播
		 * 初始状态为0
         */
        volatile int waitStatus;

        /**
         * 表示指向当前节点/线程所依赖的前置节点的链接
         *用于检查等待状态。排队时分配,且为空只有在出列时(为了GC)
         *同时,在取消前一个,我们短路时找到一个始终存在的未取消的
         *因为头节点从不被取消:节点变为只有在成功后才能使用一、
         *取消的线程永远无法成功获取,并且只有一个线程取消自身
         */
        volatile Node prev;

        /**
         * 当前节点的后继节点当前线程释放的才被唤起,在入队时分配,在绕过被取消的前	
         * 驱节点时调整,在出队列的时候取消了(GC)
		 *如果一个节点的next为空,我们可以从尾部扫描它的prev,双重检查
         * 
         */
        volatile Node next;

        /**
         *当前节点的线程,初始化使用,在使用后失效
         */
        volatile Thread thread;

        /**
        *链接到下一个节点的等待时间,或特俗的值Shared,因为条件只有独占队列时
        *所以我们只需要一个简单的连接队列在等待的时候保存节点,然后把它们转移到队	  
        *列中重新获取因为条件只能是独占性的,我们通过使用特殊的值来表示共享模式
        *
         */
        Node nextWaiter;

        /**
         * 如果该节点处于共享模式下等待直接返回true
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
        *返回当前节点的前驱节点,如果为空,直接抛出空指针异常
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    
        }

        Node(Thread thread, Node mode) {     // 指定线程和模式的构造方法
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // 指定线程和节点状态的构造方法
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

AQS具有头结点和尾节点

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

Node是构成同步队列的基础,看一下Node结构
在这里插入图片描述
同步队列中首节点是获取到锁的节点,它在释放的时候唤醒后继节点,后继节点获取到锁的时候,会把自己设置为首节点。
在这里插入图片描述
state变量的操作api

private volatile long state;
 protected final long getState() {
        return state;
    }
    protected final void setState(long newState) {
        state = newState;
    }
    /**
    *cas来操作state,依赖于底层的Unsafe类来进行
    */
    protected final boolean compareAndSetState(long expect, long update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapLong(this, stateOffset, expect, update);
    }
/*
* 独占锁的方式获取锁
*/
public final void acquire(long arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

通过调用acquire的方法获取同步状态,该方法忽略中断,线程获取同步状态失败后,进入同步队列,在对其进行中断操作后,线程不会从同步队列移除。首先调用tryAcquire方法获取同步状态,AQS并没有实现这个方法,具体的实现由它的继承类进行重写,比如ReentrantLock的Sync类。如果获取同步状态成功的直接返回true;如果获取同步状态失败的话,调用addWaiter方法把线程封装成一个Node节点添加到同步队列的尾部,最后调用acquireQueued方法使节点以自旋的方式获取同步状态,如果获取同步状态失败,要挂起线程,最后,线程如果在获取同步状态中和同步队列中被中断过,要进行自我中断。

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // 尝试快速入队
        Node pred = tail;//当前的队列节点赋值给pred
        if (pred != null) { //末尾节点不为空
            node.prev = pred;//把pred作为node的前继节点
            if (compareAndSetTail(pred, node)) {//利用CAS把node作为尾节点
                pred.next = node;//把node作为pred的后继节点
                return node;//直接返回node
            }
        }
        enq(node);
        return node;
    }
    /**
    *通过自旋的方式把node插入到队列中
    */
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // 如果t为空,说明队列为空,必须初始化
                if (compareAndSetHead(new Node()))//新建一个节点利用CAS为头结点,就是这样的方式
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

继续看一下acquireQueued的方法

/*
*主要是通过自旋的方式获取同步状态
*/
final boolean acquireQueued(final Node node, long arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;//默认线程没有被中断过
            for (;;) {
                final Node p = node.predecessor();//获取该节点的前驱节点P
                if (p == head && tryAcquire(arg)) {//如果p是头节点并且能获取到同步状态
                    setHead(node);//如果当前节点为头节点
                    p.next = null; // help GC
                    failed = false;//标志---表示成功获取同步状态,默认为true,表示失败
                    return interrupted;//返回该线程在获取同步状态过程中有没有被中断过
                }
                if (shouldParkAfterFailedAcquire(p, node) &&//用于判断是否挂起当前线程
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)//如果fail为true,直接移除当前节点
                cancelAcquire(node);
        }
    }


private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;//获取当前节点的前驱节点为waitStatus
        if (ws == Node.SIGNAL)
            /*
             *如果前驱节点的ws = signal,表示前驱节点释放后唤起当前线程,
             *可以安全的挂起当前线程
             */
            return true;
        if (ws > 0) {
            /*
             *当前前驱节点ws > 0,说明ws=cancel,表示前驱线程被取消,
             *从前驱节点继续往前遍历,直到找到第一个前驱节点的ws <= 0为止
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             *这种情况表示前驱节点的 ws = 0 或者 ws = PROGATE,我们需要一个signal
             *但是不能挂起当前线程
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

这段代码用来检测是否挂起当先线程,分三种情况,第一种情况是前驱节点的 ws = singal,表示前驱节点释放同步状态的时候会唤醒当前节点,可以安全挂起当前线程;第二种情况是前驱节点被取消,那就从前驱节点继续往前遍历,直到往前找到第一个ws <= 0 的节点;第三种是前驱节点的 ws = 0,表示前驱节点获取到同步状态,当前线程不能挂起,应该尝试去获取同步状态,前驱节点的同步状态的释放正好可以让当前节点进行获取,所以使用CAS把前驱节点的ws设为singal,另外如果 ws =PROPAGATE,说明正以共享模式进行传播,也需要使用CAS把ws设为singal.

在shouldParkAfterFailedAcquire返回true的情况下,继续看parkAndCheckInterrupted方法

 private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

调用LockSupport的park方法挂起当前线程,返回该线程是否被中断过,如果被中断过,直接设置interrupted = true.

如果获取同步状态失败,采用cancelAcquire方法取消当前节点

 /**
     *取消当前节点
     *
     */
    private void cancelAcquire(Node node) {
        //当前节点不存在的话,则忽略
        if (node == null)
            return;

        node.thread = null;//当前节点设置为null

        // 获取当前节点的prev
        Node pred = node.prev;
        while (pred.waitStatus > 0)//如果pred的ws>0,直接跳过pred继续往前遍历,直到pred
            node.prev = pred = pred.prev;

       //获取pred的后继predNext
        Node predNext = pred.next;

        //把node的ws设置为取消状态
        node.waitStatus = Node.CANCELLED;

        // 如果node是尾部节点,则全部取消
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            // pred不是头结点 && pred的线程不为空 && pred.ws = singal
            // 利用CAS把node的next设为pred的next节点
            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
        }
    }

分三种情况进行考虑
1、node本身就是尾节点,直接把node的prev设为尾节点。
2、node的prev不是头节点,直接把prev和node的next进行连接。
3、node的prev是头节点,使用unparkSuccessor唤醒后继节点。

private void unparkSuccessor(Node node) {
        /*
         * 获取node的ws,如果ws<0,使用CAS把node的ws设置为0,表示释放同步状态
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * 获取node的后继节点,根据条件s=null或者s.waitStatus>0,从同步队列的尾部开始
         * 直到找到距node最近的满足ws <= 0的节点t,把t赋给s,唤醒s节点的线程
         * 如果s不为null && s的ws <= 0,直接唤醒s的线程。
         */
        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);
    }

下面就是释放同步状态

/*
*以独占模式释放同步状态,当前线程释放同步状态的时候,会唤醒同步队列上的后继节点
*释放成功之后直接返回true
*/
public final boolean release(long arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

获取同步状态的其他方法
1、响应式中断获取同步状态

/*
*当线程被中断后,直接抛出异常,否则的话,在此调用tryAcquire方法
*/
public final void acquireInterruptibly(long arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
/*
*以独占模式获取同步状态,线程中断直接抛出异常
*/
 private void doAcquireInterruptibly(long arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

2、指定时间获取同步状态

public final boolean tryAcquireNanos(long arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }
private boolean doAcquireNanos(long arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

共享式
独占式与共享式最大的不同就是在同一个时刻能够有多个线程获取同步状态,通过调用acquireShared方法获取同步状态

public final void acquireShared(long arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
     private void doAcquireShared(long arg) {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    long r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

tryAcquireShared是留给子类去重写的,如果tryAcquireShared方法返回值<0,说明获取同步状态失败,执行doAcquireShared方法,在doAcquireShared再次调用tryAcquireShared方法,判断其返回值,若返回值<0,获取同步状态失败,需要进入同步队列进行等待,若返回值 >= 0,如果返回值=0,说明当前线程获取同步状态成功,其他线程无法获取,也就不需要唤醒它的后继节点进行传播.如果返回值>0,此时当前线程获取同步状态后要唤醒它的后继节点,让其他线程也尝试去获取同步状态.

 private void setHeadAndPropagate(Node node, long propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        /*
         * Try to signal next queued node if:
         *   Propagation was indicated by caller,
         *     or was recorded (as h.waitStatus either before
         *     or after setHead) by a previous operation
         *     (note: this uses sign-check of waitStatus because
         *      PROPAGATE status may transition to SIGNAL.)
         * and
         *   The next node is waiting in shared mode,
         *     or we don't know, because it appears null
         *
         * The conservatism in both of these checks may cause
         * unnecessary wake-ups, but only when there are multiple
         * racing acquires/releases, so most need signals now or soon
         * anyway.
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
 private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

AQS主要实现了独占式和共享式,独占式无非就是同步状态在0与1之间切换,同一时刻只有一个线程获取锁进行操作,其他线程挂起,ReentrantLock就是个经典的独占式锁.共享式的PROPAGATE的数值>0,可以使同一时刻有多个线程获取锁,如果PROPAGATE<0,则就变成了共享模式,可以参照

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值