AbstractQueuedSynchronizer

AbstractQueuedSynchronizer
lock相关操作
  1. public final void acquire(int arg)

    1. 这个函数的作用是获取同一时间段只能被一个线程获取的量,这个量就是抽象化的锁的概念
    2. 首先会执行tryAcquire方法尝试获取“锁”,如果获取到了就没事了,如果没有获取到会执行下面的操作
    3. 如果没有获取到“锁”会执行addWaiter方法,会给当前线程创建一个节点,然后将其加入到等待队列中
    4. 然后执行acquireQueued方法,继续尝试获取锁
    5. 如果一直获取不到,会执行selfInterrupt方法,该方法实现只有一句Thread.currentThread().interrupt();
  2. protected boolean tryAcquire(int arg)

  3. 这个方法需要子类自行实现,总之含义就是尝试获取“锁”,具体实现在实现类中有写

  4. private Node addWaiter(Node mode)

  5. 首先会调用Node(Thread thread, Node mode)构造,保存当前线程信息以及模式(共享或独占模式)

  6. 获取队列尾节点

  7. 将待添加的节点的pred置为队尾

  8. 执行compareAndSetTail方法将尾指针指向待添加节点

  9. 如果成功的话再将旧的尾节点的next指向待添加节点

  10. 添加成功返回节点

  11. 如果cas失败,需要执行enq方法使用更为严谨的方式来入队

  12. compareAndSetTail

    1. 该方法中调用了unsafe.compareAndSwapObject方法,就是比较并交换算法
  13. enq

    1. 死循环一直尝试将待添加节点添加到队尾
    2. 标准的CAS无锁算法。。。死循环
  14. final boolean acquireQueued(final Node node, int arg)

  15. 在执行完addWaiter后,线程被加入到阻塞队列中,但是当前线程还没有真正的阻塞,addWaiter返回了待阻塞线程的节点,就是这个参数node

  16. 由于进入阻塞状态的话效率比较低,所以AQS会尽量避免尝试获取线程独占变量的线程进入到阻塞状态,所以这个新节点刚加入队列中时,会使用一个死循环来判断这个节点是否已经在队首,如果在队首就可以尝试获取这个独占变量了,但是如果不在队首或者再次尝试获取独占变量时失败了,会调用shouldParkAfterFailedAcquire方法判断这个线程是否应该进入阻塞状态,如果当前节点的前驱节点已经进入阻塞状态了,那这个新节点也就没有必要一直判断了,空空浪费CPU资源,所以会调用parkAndCheckInterrupt方法来进入阻塞状态

  17. 方法具体实现

    1. final boolean acquireQueued(final Node node, int arg) {
          boolean failed = true;//该变量用于判断获取锁是否失败
          try {
              boolean interrupted = false;//该变量用于对调用者声明是否需要执行selfInterrupt中断线程
              //一个死循环,直到已经获取锁或者需要中断了
              for (;;) {
                  final Node p = node.predecessor();//取得当前节点的前驱节点
                  //如果前驱节点就是头节点,就说明下一个可以获取锁的就是当前节点
                  //然后调用tryAcquire方法继续尝试获取锁
                  if (p == head && tryAcquire(arg)) {
                      //如果尝试获取到了锁,会把当前节点设置为头节点
                      setHead(node);
                      //将前驱节点置空,避免强引用导致无法GC
                      p.next = null; // help GC
                      //失败标志位设置为false
                      failed = false;
                      //未修改过interrupted变量,所以返回的是false,即不需要进行中断
                      return interrupted;
                  }
                  //执行到这里的话说明上面的判断未成立,即当前节点还不能获取锁
                  //会执行shouldParkAfterFailedAcquire方法判断当前线程需不需要进入阻塞状态
                  //如果shouldParkAfterFailedAcquire为真表示需要进入阻塞状态,会调用parkAndCheckInterrupt方法进入阻塞状态
                  //如果不需要进行阻塞操作,则继续进行for循环
                  if (shouldParkAfterFailedAcquire(p, node) &&
                      parkAndCheckInterrupt())
                      //进入阻塞状态后通知调用者中断该线程
                      interrupted = true;
              }
          } finally {
              //中途抛出任何异常,都表示获取锁失败,此时需要调用cancelAcquire来取消获取锁
              if (failed)
                  cancelAcquire(node);
          }
      }
      private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
          int ws = pred.waitStatus;//前驱节点的等待状态
          if (ws == Node.SIGNAL)
       		//前驱结点在等待独占变量释放的通知,所以当前节点可以进行阻塞
              return true;
          if (ws > 0) {
              //代表前一个节点取消了获取独占变量,可以跳过前驱结点
              //在前驱结点一直处于取消状态的情况下,循环尝试将自己挂在前驱的前驱的后面,即跳过前驱结点
              do {
                  node.prev = pred = pred.prev;
              } while (pred.waitStatus > 0);
              pred.next = node;
          } else {
              //否则通过CAS将前驱结点的状态修改为等待独占变量释放
              compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
          }
          //上述操作执行后返回不需要阻塞
          return false;
      }
      private final boolean parkAndCheckInterrupt() {
          //该方法会将当前线程进入阻塞状态,并返回当前线程是否已经中断
          LockSupport.park(this);//该方法将AQS对象传入,阻塞当前线程
          return Thread.interrupted();
      }
      public static void park(Object blocker) {
          Thread t = Thread.currentThread();//获取当前线程
          setBlocker(t, blocker);//设置阻塞对象,可以记录线程是被谁阻塞的
          UNSAFE.park(false, 0L);//该方法会让当前线程不再被CPU调度
          setBlocker(t, null);
      }
      
unlock相关操作
  1. 调用relase方法
  2. 先调用tryRelease来释放独占性变量。如果成功,那么就看一下是否有等待锁的阻塞线程,如果有,就调用unparkSuccessor来唤醒他们
  3. tryRelease中的逻辑也体现了可重入锁的概念,只有等到state的值为0时,才代表锁真正被释放了。所以独占性变量state的值就代表锁的有无。当state=0时,表示锁未被占有,否在表示当前锁已经被占有。
  4. 调用了unpark方法后,进行lock操作被阻塞的线程就恢复到运行状态,就会再次执行acquireQueued中的无限for循环中的操作,再次尝试获取锁。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值