分析aqs源码自记

独占模式请求锁acquire()

  1. 尝试获取锁tryAcquire

    1. true => 继续运行
    2. false=>执行addWaiter,获取返回的node作为acquireQueued的参数执行
      1. acquireQueued(addWaiter())==true=>
      2. acquireQueued(addWaiter())==false=>
  2. addWaiter

    1. 封装当前结点,判断tail是否为空
      1. true=>直接执行enq用自旋方式初始化队头head,并在下一次循环cas(tail,node)将tail设置为node,此时node在队尾
      2. false=>将tail的next设置为node,并执行enq(),cas(tail,node),此时node在队尾。
    2. 返回node
  3. acquireQueued

    1. failed = true; interrupted = false;
    2. 进入自旋循环
    3. 判断前驱是否为head
      1. true=>尝试获得锁tryAcquire
        1. true=>设置当前node为head,设置failed=false,返回interrupted的值
      2. false=>检查node是否需要阻塞shouldParkAfterFailedAcquire
        1. true=>执行parkAndCheckInterrupt()阻塞线程,并返回阻塞结果
        2. 阻塞结果后,设置interrupted = true
  4. shouldParkAfterFailedAcquire

    1. 前驱结点状态为SIGNAL时,返回true,代表需要阻塞
    2. 前驱结点状态为CANCELLED时 ,说明要删除已被撤销的前驱结点,循环把CANCELLED状态的前驱结点全部删除。返回false
    3. 前驱接点为0,CONDITION,PROPAGATE状态下时,需要被设置为SIGNAL,等待被唤醒。返回false

独占模式释放锁release()

  1. 尝试获取锁tryAcquire

    1. true=>判断线程队列头的状态,不为零则进入unparkSuccessor方法中断线程阻塞
    2. false=>锁释放失败
  2. 返回true,锁释放成功

  3. unparkSuccessor

    1. 头结点的waitStatus小于0(SIGNAL或CONDITION)时,用cas设置为0
    2. 寻找头结点的下一个结点Node s = head.next
      1. 该结点为空,则使用尾结点前驱查找需唤醒节点,然后赋给s
    3. 再次判断该结点s是否为空,不为空则中断阻塞。
    4. 在中断阻塞之后,其实就是继续运行acquireQueued中的parkAndCheckInterruptThread.interrupted方法得到true,并将interrupted赋为true并且进入acquireQueued自旋的下一个循环时,会将头结点后移。抛弃原本的head。
# java 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×