线程基础知识04- 队列同步器(AbstractQueuedSynchronizer)

参考书:《java并发编程艺术》

参考博客:https://www.cnblogs.com/micrari/p/6937995.html

源码分析:活在夢裡 大佬写的很详细,也是我看过的AQS源码分析最好的博客了。

我就自己学习和理解的过程做一下记录。

1 数据存储结构

    AQS的数据存储,是通过内部的Node对象存储Thread,并标识线程的状态;多个Node组成的双向链表结构;

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;

    // 等待状态:条件等待。存放在条件队列,调用signal()方法或signalAll()方法,会将该节点转移至同步队列中。
    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() {
    }

    Node(Thread thread, Node mode) {
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) {
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

2.独占锁方法

大致的过程:

  • 成功获取锁,执行线程任务,释放锁,唤醒后继node;

  • 没有获取到锁,放入到queue中,进行自旋。判断前驱为头节点时,如果是,设置当前节点为head节点,移除queue中。

  • 当node自旋中出现iterruptException时,节点移除queue.

2.1 独占锁获取

aquire方法

  • 1.tryAcquire(arg)方法获取独占锁,由AQS继承类实现

  • 2.如果获取独占锁成功则返回,否则加入queue中

public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();//中断当前线程
    }


 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;//设置节点的前置节点是tail节点
            if (compareAndSetTail(pred, node)) {//通过CAS将新加入的节点,设置成末尾节点
                pred.next = node;
                return node;
            }
        }
       //队列初始化,node设置为tail节点
        enq(node);
        return node;
    }
 
 /**
  * 作用:将当前节点加入到queue
  * 1.死循环,只有当节点添加到末尾时才退出
  * 2.通过CAS进行节点操作,类似“串行”执行 
  **/
 private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // 如果queue为空
                if (compareAndSetHead(new Node()))//通过CAS,增加空的头节点
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {//通过CAS,结尾节点进行关联
                    t.next = node;
                    return t;
                }
            }
        }
    }  
   
 /**
  * 作用:循环等待,当前节点获取到锁,或者当前节点中断;
  *  1.如果前驱是头节点并且获取到了锁,则设置当前节点为头节点
  *  2.如果没有获取到锁,则变更pre节点为SINGLE,当前线程进行阻塞
  **/
  final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {//一致等待队列中的任务,直到当前任务为头节点
                final Node p = node.predecessor();//当前节点的前一个节点
                if (p == head && tryAcquire(arg)) {//前一个节点是头节点,并且获取到锁的时候
                    setHead(node);//将当前节点设置为头节点
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                  /**
                   * 检查前一个节点的状态,如果为Single
                   * parkAndCheckInterrupt方法把当前线程阻塞
                   **/   
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {// 当出现interruptedException的时候,不对异常处理,但是对当前线程所对应的当前节点的状态变更为Cancelled
            if (failed)
                cancelAcquire(node);//变更当前节点状态
        }
    }


  /**
   * 作用:将node节点的前置节点,状态变成SIGNAL,如果是CANCELLED则移除
   **/ 
  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) {//是cancelled状态只能跳过,找到node节点前面的非Cancelled状态的节点
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {//设置前驱状态为SIGNAL,可以获取锁的状态
            /*
             * 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;
    }
/**
 * 1.阻塞当前线程
 * 2.判断当前线程是否中断
 **/
 private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

/**
 * 作用:链表中取消节点,并更改节点状态
 **/
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前节点为非cacelled状态
            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)) {//如果是node是为节点,替换尾节点为node的pre
            compareAndSetNext(pred, predNext, null);//更改pre节点的后继节点为空
        } 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) {//pred节点不是头节点,不是Callced状态,
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                  //pred==head,唤醒后继锁操作
                unparkSuccessor(node);
            }

            node.next = node; // help GC
        }
    }



执行的逻辑大致如下图:

acquireInterruptibly方法

  • 这个方法和acquire方法最大的区别在于,会抛出InterruptException
 //如果中断了,抛出异常
  public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
 private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
       //省略部分代码。。。。。。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
      //省略部分代码。。。。。。
    }

2.2 独占锁释放

执行的过程:

  • 释放锁操作,成功则唤醒后继节点

  • 释放锁失败,则返回false

/**
 * 1.tryRelease方法子类进行重写;
 * 2.释放锁成功后,唤醒后继node
 **/
public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
/**
 * 作用:唤醒后继节点
 **/ 
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;//节点的状态变成0,后续等待的线程可以对状态进行调整
        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.
         *   
         * 从tail查找非null和非cancelled状态的node的后继节点
         */
        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);
    }

3.共享锁方法

3.1 共享锁获取

acquireShared方法

  • 如果节点是取消状态,则直接跳过;如果不是取消状态,增加到链表中。

  • 当前节点的前驱是头节点,获取共享锁>=0,设置当前节点为头节点。

  • 当前节点是否传播(propagation>0)和下个节点是否共享来判断是否唤醒下个节点。

 /**
  * 返回-1表示没有获取到共享锁
  * 不会捕获InterruptException
  **/
 public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }


   /**
    * 和独占式锁代码类似
    */   
   private void doAcquireShared(int arg) {
        final Node node = addWaiter(Node.SHARED);//在链表末尾增加一个共享节点
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    /**
                     * 当前节点的前置节点获取到锁;
                     * 把当前节点设置为头节点;
                     **/  
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                /**
                 * 判断p前置节点状态,阻塞当前线程并判断当前线程中断装填  
                 **/ 
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {//非正常结束进行移除
            if (failed)
                cancelAcquire(node);
        }
    }

 //设置共享锁头节点,唤醒下一个节点
 private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        //
        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() {
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {//存在后继节点
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))//头节点为SIGNAL,则变成0状态
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))//如果为0状态,则变成PROPAGATE状态
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // 如果头节点发生变化了,就推出循环
                break;
        }
    }

// 如果存在后继节点,则唤醒后继节点
  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);
    }

3.2 共享锁释放

//判断有无获取到锁,获取到锁则唤醒后继节点
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            //这一块代码和上面增加共享节点的一样。
            doReleaseShared();
            return true;
        }
        return false;
    }

Condition子类

条件队列插入

await方法

  • 当前线程中断则报错退出

  • 创建节点增加到条件队列

  • 释放互斥锁

  • 没有在同步队列就进行阻塞

  • 检测是否发生中断,是更新完状态加入到同步队列时发生或是调用signal方法更改状态

  • 如果有中断状态,则抛异常或者中断当前线程

/**模式意味着在退出等待时重新中断  */
private static final int REINTERRUPT =  1;
/** 退出等待时抛出Interrupt异常 */
private static final int THROW_IE    = -1;

 public final void await() throws InterruptedException {
            if (Thread.interrupted())//当前线程异常,抛出异常
                throw new InterruptedException();
            /**
             * 1.最后节点lasterWaiter关联nextWaiter关联当前节点
             * 2.将当前节点设置为lastWaiter 
             **/         
            Node node = addConditionWaiter();
            /**
             * 1.释放当前状态
             * 2.如果释放成功,则调整当前节点状态为释放
             **/     
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            /**
             * 判断是否在同步队列,如果在同步队列则跳过,不在同步队列,就阻塞
             **/     
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            /**
             * 获取互斥锁中,如果中断,但是中断异常不抛出,设置成REINTERRUPT
             **/ 
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            //当前节点的nextWaiter不为空,则清除cancelled状态的节点
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
           //THROW_IE抛出中断异常,REINTERRUPT中断当前线程
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

/**
 * 作用:增加当前线程到条件队列
 */
private Node addConditionWaiter() {
            Node t = lastWaiter;//
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {//如果最后一个节点的waitStatus不是CONDITION,删除CANCELLED的节点,重新定位最后一个条件节点
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;//将当前节点设置为尾节点
            return node;
        }


 /**
  * 作用:判断是否在同步队列中
  **/
 final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null) 
            return false;
        if (node.next != null) // 有后继节点,一定在同步队列中
            return true;

        return findNodeFromTail(node);//在同步队列中查找,是否有当前节点
    }

/**
 * 判断中断状态:
 * THROW_IE表示加入到同步队列中;
 * REINTERRUPT表示调用了SINGLE()方法,状态已经更新过了;
 * 0表示线程中断了
 **/ 
private int checkInterruptWhileWaiting(Node node) {
            return Thread.interrupted() ?
                (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
                0;
        }

/**
 * 作用:变更节点状态,节点加入同步队列进行判断
 **/
final boolean transferAfterCancelledWait(Node node) {
        /**
         * 将CONDITION状态变成准备状态,
         * 将节点加到同步队列尾端
         */
        if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
            enq(node);
            return true;
        }
        /**
         * 调用了single()方法,状态已经被变更了,所以要等待加入到同步队列中  
         */
        while (!isOnSyncQueue(node))
            Thread.yield();//当前线程让步执行
        return false;
    } 

//THROW_IE抛出中断异常,REINTERRUPT中断当前线程
 private void reportInterruptAfterWait(int interruptMode)
            throws InterruptedException {
            if (interruptMode == THROW_IE)
                throw new InterruptedException();
            else if (interruptMode == REINTERRUPT)
                selfInterrupt();
        }

signal方法

  • 判断当前线程独占锁的持有

  • 如果持有独占锁,则将头节点加入到同步队列中

  • 头节点的同步队列中前驱节点如果是取消状态,则放行进行锁竞争

 /**
  * isHeldExclusively方法由子类实现。用于检查持有互斥锁的线程是不是当前线程
  * 将第一个节点增加到同步队列 
  **/
 public final void signal() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignal(first);
        }
 /**
  * 头节点移动到同步队列
  */
  private void doSignal(Node first) {
            do {
                if ( (firstWaiter = first.nextWaiter) == null)//如果只有一个节点,则lastWaiter变成空
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);//头节点已经状态已经取消则结束循环
        }
 /**
  * 作用:是将头节点增加到同步队列,如果头队列的前驱节点已经是取消状态,或无法调整为singal状态,则进行锁竞争
  */
    final boolean transferForSignal(Node node) {
        /*
         * 如果waitStatus不能改变,说明这个节点已经是Cancelled
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * 将节点增加到同步队列,判断前驱节点的waitStatus,如果是Cancelled或者CAS自旋无法把状态设置为SINGAL,
         * 放行节点线程进行锁竞争 
         */
        Node p = enq(node);//返回node节点的前驱
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }


signalALL方法

  • 和signal方法相似,不同的是将所有条件队列转移到同步队列
  public final void signalAll() {
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            Node first = firstWaiter;
            if (first != null)
                doSignalAll(first);
        }

        private void doSignalAll(Node first) {
            lastWaiter = firstWaiter = null;
            do {
                Node next = first.nextWaiter;
                first.nextWaiter = null;
                transferForSignal(first);
                first = next;
            } while (first != null);
        }


posted @ 2020-09-07 14:27  PerfectLi  阅读(192)  评论(0编辑  收藏  举报