公平锁/非公平锁分析,从await/singl分析AQS

 大家推荐个靠谱的公众号程序员探索之路,公众号内点击网赚获取彩蛋,大家一起加油,这个公众号已经接入图灵https://i-blog.csdnimg.cn/blog_migrate/93320939ba8f8b0a898e29429753f496.png ​  

   前言:看这篇文章之前需要看https://blog.csdn.net/yueloveme/article/details/86483781,下文有些名词和这篇文章统一
   1.ReentrantLock 的公平锁和非公平锁
   代码上的区别
   非公平锁:
   这里直接cas 看能不能获取锁
   final void lock() {
      if (compareAndSetState(0, 1))
         setExclusiveOwnerThread(Thread.currentThread());
      else
         acquire(1);
   }

   public final void acquire(int arg) {
      if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
         selfInterrupt();
   }

   protected final boolean tryAcquire(int acquires) {
      return nonfairTryAcquire(acquires);
   }

   //锁状态为可用 直接cas获取锁
   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;
   }

   公平锁:
   final void lock() {
      acquire(1);
   }

   public final void acquire(int arg) {
      if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
         selfInterrupt();
   }

   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;
   }

   区别:
   非公平锁的lock方法会尝试获取一次锁
         tryAcquire方法在尝试获取锁的时候, 不用考虑等待队列中是否有节点, 直接cas获取锁
   2.从condition.await/.signal看AQS
2.1condition.await有一个条件队列(单项链表),await就放在末端,signal就把对首的节点放到等待队列末尾

   /**
    * Implements interruptible condition wait.
    * <ol>
    * <li> If current thread is interrupted, throw InterruptedException.
    * <li> Save lock state returned by {@link #getState}.
    * <li> Invoke {@link #release} with saved state as argument,
    * throwing IllegalMonitorStateException if it fails.
    * <li> Block until signalled or interrupted.
    * <li> Reacquire by invoking specialized version of
    * {@link #acquire} with saved state as argument.
    * <li> If interrupted while blocked in step 4, throw InterruptedException.
    * </ol>
    */
   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)) {
         LockSupport.park(this);
         if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
      }
      if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
         interruptMode = REINTERRUPT;
      if (node.nextWaiter != null) // clean up if cancelled
         unlinkCancelledWaiters();
      if (interruptMode != 0)
         reportInterruptAfterWait(interruptMode);
   }


   /**
    * Adds a new waiter to wait queue.
    *
    * @return its new wait node
    * 方法目的:将新节点加入到条件队列末尾
    * 方法思路:
    * 1.检查末尾是不是 取消状态 是剔除
    * 2.生成新node 将新node加入到条件队列末尾 更新条件末尾为新node
    */
   private Node addConditionWaiter() {
      Node t = lastWaiter;
      // If lastWaiter is cancelled, clean out.
      //检查条件队列的末尾的节点不是condition  说明该节点已经被取消了 那么会扫描条件队列中其他已经被取消的节点并剔除掉
      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;
   }

   addConditionWaiter方法中有一个剔除条件队列中等待节点的方法unlinkCancelledWaiters

   /**
    * 方法目的:将取消的节点从条件队列中剔除掉
    * 就是纯链表操作 trail标记符合条件的队列的末尾节点  遍历条件队列 等待状态不是CONDITION就剔除掉
    */
   private void unlinkCancelledWaiters() {
      Node t = firstWaiter;
      Node trail = null;
      while (t != null) {
         Node next = t.nextWaiter;
         if (t.waitStatus != Node.CONDITION) {
            t.nextWaiter = null;
            if (trail == null)
               firstWaiter = next;
            else
               trail.nextWaiter = next;
            if (next == null)
               lastWaiter = trail;
         } else
            trail = t;
         t = next;
      }
   }

   完全释放锁 为什么完全释放锁, 因为有重入锁的情况锁状态可能大于1
   这行代码:
   int savedState = fullyRelease(node);
   返回的是之前的锁状态值

   /**
    * Invokes release with current state value; returns saved state.
    * Cancels node and throws exception on failure.
    *
    * @param node the condition node for this wait
    * @return previous sync state
    * 方法目的:完全释放锁
    * 方法思路:
    * 1.获取锁状态
    * 2.释放锁 成功返回之前获取的锁状态值
    * 3.失败抛出IllegalMonitorStateException异常 并将该节点的等待状态设置为Node.CANCELLED;也就是取消状态
    */
   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;
      }
   }

   //自旋判断节点是否已经在等待队列中  因为signal(唤醒方法会将node放入到等待队列中)
   如果没在队列中 线程挂起
   int interruptMode = 0;
         while(!

   isOnSyncQueue(node))

   {
      LockSupport.park(this);
      if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
         break;
   }

   /**
    * 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
    * 方法目的:判断当前节点是否已经在等待队列中
    * 方法思路:
    * 1.如果 当前节点的等待状态为Node.CONDITION 获取前驱节点为null 说明没有在等待队列中
    * 2.如果后继节点不为空那么说明在等待队列中
    * 注意:不能通过node.prev != null来判断node已经在等待队列中了,因为在添加到等待队列的时候会
    * 先设置node.prev = tail 然后cas tail为node 成功后在设置原tail.next = 现在的tail
    */
   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.
         */
      return findNodeFromTail(node);
   }

   /**
    * Returns true if node is on sync queue by searching backwards from tail.
    * Called only when needed by isOnSyncQueue.
    *
    * @return true if present
    * 方法目的:从队列末尾遍历到head看是否 在等待队列中
    */
   private boolean findNodeFromTail(Node node) {
      Node t = tail;
      for (; ; ) {
         if (t == node)
            return true;
         if (t == null)
            return false;
         t = t.prev;
      }
   }

   2.2signal 方法

   /**
    * 方法目的:将条件队列的首节点 放入等待队列末尾
    * 方法思路:
    * 1.检测获取锁没有 没有抛出异常
    * 2.放入等待队列
    */
   public final void signal() {
      if (!isHeldExclusively())
         throw new IllegalMonitorStateException();
      Node first = firstWaiter;
      if (first != null)
         doSignal(first);
   }

   /**
    * Removes and transfers nodes until hit non-cancelled one or
    * null. Split out from signal in part to encourage compilers
    * to inline the case of no waiters.
    *
    * @param first (non-null) the first node on condition queue
    *              方法目的: 从首节点 开始将符合条件的第一个节点放入到 等待队列中
    *              方法思路:
    *              first = first.nextWaiter
    *              如果首节点的下一个节点为null 说明末尾节点为null  首节点的下一个节点要和条件队列断联系 因为首节点要移到等待队列中
    *              循环判断如果first移动失败 那么 就移动first的后一个
    */
   private void doSignal(Node first) {
      do {
         if ((firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
         first.nextWaiter = null;
      } while (!transferForSignal(first) &&
            (first = firstWaiter) != null);
   }

   /**
    * Transfers a node from a condition queue onto sync queue.
    * Returns true if successful.
    *
    * @param node the node
    * @return true if successfully transferred (else the node was
    * cancelled before signal)
    */
   final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
   如果node的waitStatus 不为Node.CONDITION  那么说明节点已经取消
   那么就不需要转移了  返回false
   否则 设置为0
         */
      if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
         return false;

        /*
         * 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).
         */
      //注意这个和 lock的enq是同一个方法 将node放入到等待队列中 并且返回node的前驱节点
      Node p = enq(node);
      int ws = p.waitStatus;
      //如果前驱节点 的等待状态大于0 表示已经取消 那么唤醒node的线程  唤醒后会接着执行上面await挂起线程的地方
      //如果ws <= 0 那么需要把p 的waitStatus 置为-1
      //如果cas p的waitStatus 设置为-1 失败那么也会唤醒该node的线程
      if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
         LockSupport.unpark(node.thread);
      return true;
   }

   正常情况下,ws >0||!

   compareAndSetWaitStatus(p, ws, Node.SIGNAL) 这句中,ws <=0,

   而且 compareAndSetWaitStatus(p, ws, Node.SIGNAL) 会返回 true,
   所以一般也不会进去 if
   语句块中唤醒 node
   对应的线程。然后这个方法返回 true,
   也就意味着 signal
   方法结束了,节点进入了阻塞队列。
   假设发生了阻塞队列中的前驱节点取消等待,
   或者 CAS
   失败,只要唤醒线程,让其进到下一步即可


   3.//唤醒之后会从这里挂起的地方继续执行
   int interruptMode = 0;
   while(!

   isOnSyncQueue(node))

   {
      LockSupport.park(this);
      if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
         break;
   }

   唤醒之后会检查 线程的中断状态
   判断是signal之前中断 还是signal之后中断

   /**
    * Checks for interrupt, returning THROW_IE if interrupted
    * before signalled, REINTERRUPT if after signalled, or
    * 0 if not interrupted.
    * 如果没有中断返回0
    * 中断了如果是signal之前中断的返回THROW_IE
    * 如果是signal之后中断的返回REINTERRUPT
    */
   private int checkInterruptWhileWaiting(Node node) {
      return Thread.interrupted() ?
            (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
            0;
   }

   /**
    * Transfers node, if necessary, to sync queue after a cancelled wait.
    * Returns true if thread was cancelled before being signalled.
    *
    * @param node the node
    * @return true if cancelled before the node was signalled
    */
   final boolean transferAfterCancelledWait(Node node) {
      //如果是signal之前中断的 那么这一步会cas成功  如果signal先发生的话 会把node的waitStatus设置为0 transferForSignal方法的第一个if语句里
      if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
         //可以看到即使是中断了也会把node放到等待队列的末尾
         enq(node);
         return true;
      }
        /*
         * If we lost out to a signal(), then we can't proceed
         * until it finishes its enq().  Cancelling during an
         * incomplete transfer is both rare and transient, so just
         * spin.
   如果到这里说明 前面cas失败 也就是说 node的awaitStatus已经是0 那么只需要把node放在等待队列末尾就行
         */
      while (!isOnSyncQueue(node))
         Thread.yield();
      return false;
   }

   interruptMode 的取值说明
   REINTERRUPT  1表示await之后需要中断
   THROW_IE -1
   表示await之后 抛出中断异常
   0表示不要处理
//到这里大家就会明白了 这个while循环 要么 node在等待队列中了 要么中断了才会结束循环
while(!

   isOnSyncQueue(node))

   {
      LockSupport.park(this);
      if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
         break;
   }
   //这一步如果线程中断了 而且不是signal之前中断的 那么interruptMode = REINTERRUPT;
   if(

   acquireQueued(node, savedState) &&interruptMode !=THROW_IE)
   interruptMode =REINTERRUPT;

   /**
    * Acquires in exclusive uninterruptible mode for thread already in
    * queue. Used by condition wait methods as well as acquire.
    *
    * @param node the node
    * @param arg  the acquire argument
    * @return {@code true} if interrupted while waiting
    * 这个方法和 .lock 没有获取锁 然后把node放入等待队列中是同一个方法
    * 简单描述一下,如果这个节点刚好是等待队列的第一个节点那么可以尝试获取一下锁,
    * 如果不是 那么把前驱节点的waitStatus设置为-1  然后node的线程挂起
    */
   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;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                  parkAndCheckInterrupt())
               interrupted = true;
         }
      } finally {
         if (failed)
            cancelAcquire(node);
      }
   }
   //如果这一步的node.nextWaiter != null说明是signal之前中断的 因为如果中断了 而且是signal之前那么也会把node加入到等待队列的末尾
   //但是没有node.nextWaiter == null这一步    signal之前没有中断的再加入到等待队列的时候都会将条件队列的node与条件队列断开联系
   //如果没有断开联系说明条件队列中节点的waitStatus不全是CONDITION所以需要清理
   if(node.nextWaiter !=null) // clean up if cancelled

   unlinkCancelledWaiters();

   //如果interruptMode != 0 那么说明有中断需要处理
   if(interruptMode !=0)

   reportInterruptAfterWait(interruptMode);

   /**
    * Throws InterruptedException, reinterrupts current thread, or
    * does nothing, depending on mode.
    * 这个方法很明显的看到 如果是 signal前  THROW_IE  -1 会抛出中断异常
    * signal后  REINTERRUPT 1 会执行中断
    */
   private void reportInterruptAfterWait(int interruptMode)
         throws InterruptedException {
      if (interruptMode == THROW_IE)
         throw new InterruptedException();
      else if (interruptMode == REINTERRUPT)
         selfInterrupt();
   }

//带有超时的await
   思路:
   不带超时时间的await 是直接挂起直到被唤醒
   带有超时时间的await是休眠指定的时间 后加入到等待队列

   boolean await(long time, TimeUnit unit) throws InterruptedException;

   public final long awaitNanos(long nanosTimeout) throws InterruptedException

   public final boolean awaitUntil(Date deadline) throws InterruptedException

   看其中一个其他的都一样

   public final boolean await(long time, TimeUnit unit)
         throws InterruptedException {
      //将等待时间转换成纳秒  1000纳秒 = 1毫秒
      long nanosTimeout = unit.toNanos(time);
      if (Thread.interrupted())
         throw new InterruptedException();
      Node node = addConditionWaiter();
      int savedState = fullyRelease(node);
      //计算到什么时候超时  精确到纳秒
      final long deadline = System.nanoTime() + nanosTimeout;
      //记录是否超时
      boolean timedout = false;
      int interruptMode = 0;
      while (!isOnSyncQueue(node)) {
         //如果 剩余时间 <= 0L  那么就判断在指定时间内 有没有超时 怎么判断 如果已经有人唤醒那么说明没有超时
         //如果没有人唤醒那么加入到等待队列中返回false 说明超时
         if (nanosTimeout <= 0L) {
            timedout = transferAfterCancelledWait(node);
            break;
         }
         //spinForTimeoutThreshold = 1000纳秒 也就是1毫秒 如果超时时间小于1毫秒那么认为自旋等待就可以
         if (nanosTimeout >= spinForTimeoutThreshold)
            LockSupport.parkNanos(this, nanosTimeout);
         // 如果中断 break
         if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
         //剩余时间 = 什么时候超时 - 当前纳秒数
         nanosTimeout = deadline - System.nanoTime();
      }
      if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
         interruptMode = REINTERRUPT;
      if (node.nextWaiter != null)
         unlinkCancelledWaiters();
      if (interruptMode != 0)
         reportInterruptAfterWait(interruptMode);
      return !timedout;
   }

   // 不抛出InterruptedException异常的await
   public final void awaitUninterruptibly() {
      Node node = addConditionWaiter();
      int savedState = fullyRelease(node);
      boolean interrupted = false;
      while (!isOnSyncQueue(node)) {
         LockSupport.park(this);
         if (Thread.interrupted())
            interrupted = true;
      }
      if (acquireQueued(node, savedState) || interrupted)
         selfInterrupt();
   }



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值