AQS 学习笔记

AbstractQueuedSynchronizer(AQS)

用于构建同步工具的框架

  • ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock、SynchronousQueue、FutureTask是基于AQS实现 – 将功能委托给内部AQS子类

  • AQS提供原子式的同步状态管理 - 使用整数属性state表示资源状态具体语义有同步器定义

    • getState()
    • setState()
  • compareAndSetState()

  • AQS提供获取与释放资源的操作,并维护一个线程的等待队列。AQS支持独占模式和共享共享两种方式访问资源,通常只在子类中实现一种模式

    • acquire()、acquireInterruptibly() 通过调用子类实现的tryAcquire()进获取操作,tryAcquire()返回true时,表示获取成功;获取失败时,将线程阻塞,加入等待队列,直到tryAcquire()返回true
    • acquireShared()、acquireInterruptibly() 类似acquire()
    • release() 通过调用子类实现的tryRelease()释放; 在tryRelease返回true时,尝试取消一个或多个线程的阻塞
    • releaseShared() 同上,调用子类的tryReleaseShared()
  • acquire()的基本操作:

    while (!tryAcquire(arg)) {
        if(当前线程不在队列中) 
            将线程入队;
        	可能阻塞线程;
    }
    
  • release()的基本操作:

    if (tryRelease(arg))
        unblock队首的线程;
    
  • 以AQS为基础实现同步器:使用使用getState(),setState(), compareAndSetState()重写tryAcquire(), tryAcquireShared(), tryRelease(), tryReleaseShared(), isHeldExclusively()。使用acquire(), acquireShared(),release(), releaseShared() 构建同步器。

ReentrantLock的实现

image-20220609003703145
  • ReentrantLock下实现中不同的AQS: FairSync(用于公平锁), NonfairSync(用于非公平锁)

  • 公平锁使用FairSync实现lock()

    • 状态为0表示该锁未被持有,大于0表示该所被持有(加锁的次数)

    • tryAcquire的实现:

      protected final boolean tryAcquire(int acquires) {
          final Thread current = Thread.currentThread();
          int c = getState();
          if (c == 0) {
              // !hasQueuedPredecessors(): 没有其他线程在队列中等待acquire
              // compareAndSetState(0, acquires): 安全地改变状态
              if (!hasQueuedPredecessors() &&
                  compareAndSetState(0, acquires)) {
                  setExclusiveOwnerThread(current);	//设置独占线程
                  return true;
              }
          }
          else if (current == getExclusiveOwnerThread()) {
              // 如果该sync已经被该线程独占了,不会有其他线程竞争,可以安全的增加重入的数量
              int nextc = c + acquires;
              if (nextc < 0)
                  throw new Error("Maximum lock count exceeded");
              setState(nextc);
              return true;
          }
          return false;
      }
      
  • 非公平锁使用NonfairSync实现lock()

    • 状态为0表示该锁未被持有,大于0表示该所被持有(加锁的次数)

    • 先调用lock() 尝试立即加锁

      final void lock() {
          if (compareAndSetState(0, 1))
              //尝试立即加锁
              setExclusiveOwnerThread(Thread.currentThread());
          else
              //立即加锁失败
              acquire(1);
      }
      
    • 立即加锁失败后等待acquire(1),tryAcquire实现

      protected final boolean tryAcquire(int acquires) {
          return nonfairTryAcquire(acquires);
      }
      
      // 在NonfairSync的父类Sync中实现
      final boolean nonfairTryAcquire(int acquires) {
          final Thread current = Thread.currentThread();
          int c = getState();
          if (c == 0) {
              // 不再判断队首是否还有其他线程在等待
              if (compareAndSetState(0, acquires)) {
                  setExclusiveOwnerThread(current);
                  return true;
              }
          }
          else if (current == getExclusiveOwnerThread()) {
              int nextc = c + acquires;
              if (nextc < 0) // overflow
                  throw new Error("Maximum lock count exceeded");
              setState(nextc);
              return true;
          }
          return false;
      }
      
  • 非公平/公平锁的释放使用同一个tryRelease

    • tryLock() --> Sync.nonfairTryAcquire(1)
    • unlock() --> Sync.tryRelease(1)
      protected final boolean tryRelease(int releases) {
          int c = getState() - releases;
          if (Thread.currentThread() != getExclusiveOwnerThread())
              throw new IllegalMonitorStateException();
          boolean free = false;
          if (c == 0) {
              free = true;
              setExclusiveOwnerThread(null);
          }
          setState(c);
          return free;
      }
      
  • 创建条件队列:直接返回AQS的内部类ConditionObject

AQS底层:

等待队列
  • AQS的等待队列为CLH队列的变体–> 双端队列
  • 每个等待acquire的线程将作为一个节点在等待队列中排队
  • Node类 – 每个节点代表一个在队列中等待获取资源(acquire)的线程
    • waitStatus : 当前节点状态(表示线程的状态)
      • (0) : 初始化时的默认值
      • CANCELLED (1): 该节点所代表的线程被取消(timeout / interrupt)
      • SIGNAL (-1): 该节点的后继所表示的线程正被阻塞(表示该节点被释放或取消后,应解除(unpark)其后继的阻塞,让后面的线程acquire())(表示该节点有责任唤醒其后继节点)
      • CONDITION (-2): 该节点所代表的线程在condition上等待(该节点在条件队列中)
      • PROPAGATE (-3): 指示下一个acquireShared()要无条件的传播
    • next: 后驱指针
    • prev:前驱指针
    • thread:该节点所表示的线程
    • nextWaiter:下一个处于CONDITION状态的节点
  • 队列头节点head为哨兵(哑节点),其waitStatus有意义,表示当前获取了资源的线程的状态。队首第一个节点(head的后继),成功acquire()后,将取代旧的head。
acquire() – 以独占的方式获取
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
    acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();	//acquireQueued成功获取后,返回中断标记,在此恢复
}
  • 首先尝试加锁 tryAcquire(…)

  • 若失败则首先将当前线程加入等待队列中 addWaiter(…),返回代表该线程的节点

    //将当期线程加入等待队列
    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)
        enq(node);
        return node;
    }
    
    private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                //队列为空 尝试初始化队列,插入哨兵节点
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 加入到尾后
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
    
  • acquireQueued(…)将以独占的,不可中断的模式,尝试在该节点上"获取" (使得当前线程为独占的线程)

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //自旋
            for (;;) {
                //获取前驱p
                final Node p = node.predecessor();
                // 如果该节点(代表当前线程)是第一个有效节点(队列中第一个正在等待的节点),则尝试获取(tryAcquire)
                if (p == head && tryAcquire(arg)) {
                    //获取成功,让该节点成为哨兵节点("出队")
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                
                // 执行至此的两种可能: p != head 当前节点不是第一个有效节点; tryAcquire() 失败(被非公平地抢占了(要是公平的话不能插队,只有队首的节点才可以获取锁))
                // shouldParkAfterFailedAcquire() 判断是否应该阻塞,避免自旋占用CPU
                // 若应被阻塞,则parkAndCheckInterrupt() 阻塞当前线程,并在线程恢复后返回中断状态
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //若阻塞恢复后发现被中断
                    interrupted = true;
            }
        } finally {
            if (failed)
                //获取失败,将节点标记为取消状态
                cancelAcquire(node);
        }
    }
    
    //更新该节点和等待队列,判断是否应阻塞(返回true --> 阻塞)
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //前驱节点的等待状态
        int ws = pred.waitStatus;
        
        if (ws == Node.SIGNAL)
            //前驱的状态为SIGNAL,表示其前驱正在活跃 --> 该节点的线程被应该被阻塞,等待前驱释放或被取消
            return true;	//需要阻塞
        if (ws > 0) {
            //前驱节点状态大于0,表示其前驱节点已被取消,将其弹出
            //弹出所有被取消的前驱节点(线程)
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            //0 或 PROPAGATE
            /*
            * 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.
            */
            //将前驱状态设置为SIGNAL,表示该线程等待前驱节点释放或被取消
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
            //返回false,在阻塞前再尝试一次
        }
        return false;
    }
    
    //挂起当前线程,并返回中断标记
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
    
  • cancelAquire(node) – 获取失败,取消当前节点(线程)的获取操作

    //Cancels an ongoing attempt to acquire
    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;
    
        //在此设置为CANCELLED后,就可能被acquire()中调用的shouldParkAfterFailedAcquire()弹出
        // 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 (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            // 该节点不是尾节点的情况
            // 如果该节点被取消前不是第一个(head的后续)有效节点(在等待的),则将自己的有效前驱的状态设为SIGNAL,用以表示自己的有效后继等待唤醒
            // 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))) &&	//其有效前驱没有被设为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
        }
    }
    
release()
  • 以独占模式释放->取消一个或多个线程的阻塞
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
  • unparkSuccessor(node) – 唤醒一个node的未被取消的后继节点(线程)
    • 从尾部向前遍历寻找第一个未被取消的节点的原因:
      • addWaiter时先设置prev再设置next,并不是原子操作
      • 取消节点时,断开了被取消节点的next指针,并未断开prev指针
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)	//SIGNAL\CONDITION\PROPAGATE
        compareAndSetWaitStatus(node, ws, 0);	//设为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);
}
ConditionObject

AQS的内部类,实现了默认的条件队列

/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
  • await() – 阻塞线程,加入条件队列,等待signal()唤醒

    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        
        //添加将当前线程加入 条件队列
        Node node = addConditionWaiter();
        //释放并保存状态,也会 等待队列 唤醒下一个节点
        int savedState = fullyRelease(node);
        
        //阻塞,等待被唤醒
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {	//判断当前节点(线程),是否在等待队列中等待acquire,若是则可以跳出在condition上的等待
            LockSupport.park(this);
            
            //checkInterruptWhileWaiting:若被signal前中断,返回THROW_IE; 若被signal后中断,返回REINTERRUPT; 未被中断返回0
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        
        //acquireQueued(node, savedState): 恢复state,并等待acquire
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }
    
    //加入condition的等待队列(条件队列)
    private Node addConditionWaiter() {
        Node t = lastWaiter;
        // If lastWaiter is cancelled, clean out.
        if (t != null && t.waitStatus != Node.CONDITION) {
            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 int fullyRelease(Node node) {
        boolean failed = true;
        try {
            int savedState = getState();
            if (release(savedState)) {
                failed = false;
                return savedState;
            } else {
                throw new IllegalMonitorStateException();
            }
        } finally {
            if (failed)
                node.waitStatus = Node.CANCELLED;
        }
    }
    
    /**
    * Returns true if a node, always one that was initially placed on
    * a condition queue, is now waiting to reacquire on sync queue.
    * @param node the node
    * @return true if is reacquiring
    */
    final boolean isOnSyncQueue(Node node) {
        if (node.waitStatus == Node.CONDITION || node.prev == null)
            return false;
        if (node.next != null) // If has successor, it must be on queue
            return true;
        /*
        * node.prev can be non-null, but not yet on queue because
        * the CAS to place it on queue can fail. So we have to
        * traverse from tail to make sure it actually made it.  It
        * will always be near the tail in calls to this method, and
        * unless the CAS failed (which is unlikely), it will be
        * there, so we hardly ever traverse much.
        * (-->addWaiter中插入节点的方式)
        */
        //从尾向前搜索节点
        return findNodeFromTail(node);
    }
    
    //从尾向前寻找node,返回是否储存在
    private boolean findNodeFromTail(Node node) {
        Node t = tail;
        for (;;) {
            if (t == node)
                return true;
            if (t == null)
                return false;
            t = t.prev;
        }
    }
    
  • signal() – 唤醒一个在条件队列中等待的线程

    public final void signal() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        
        //唤醒等待队列中第一个节点(线程)
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }
    
    //移除被取消的节点,直到遇到一个未被取消的节点或null。第一个遇到的节点将会被转移至等待队列。
    private void doSignal(Node first) {
        do {
            if ( (firstWaiter = first.nextWaiter) == null)
                lastWaiter = null;
            first.nextWaiter = null;	//断开后继指针
        } while (!transferForSignal(first) &&		//直到转移成功一个未被取消的节点
                 (first = firstWaiter) != null);	//或为空
    }
    
    //将节点从条件队列转移到等待队列 (condition queue -> sync queue)
    final boolean transferForSignal(Node node) {
        /*
        * If cannot change waitStatus, the node has been cancelled.
        */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;	//在条件队列中的node可能取消---> 失败. doSignal中将继续找下一个节点
    
        /*
        * Splice onto queue and try to set waitStatus of predecessor to
        * indicate that thread is (probably) waiting. If cancelled or
        * attempt to set waitStatus fails, wake up to resync (in which
        * case the waitStatus can be transiently and harmlessly wrong).
        */
        Node p = enq(node);		//自旋尝试加入尾部 (addWaiter方法也使用enq),返回其前驱
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))	//将其前驱节点的状态改为signal(要求其释放之后,唤醒当前节点)
            LockSupport.unpark(node.thread);	//失败,则直接唤醒
        return true;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

绫零依

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

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

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

打赏作者

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

抵扣说明:

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

余额充值