您的位置:首页 > 其它

可中断获取锁与超时等待获取锁源码分析

codeFeel 2020-09-25 16:35 37 查看 https://blog.51cto.com/1484943

可中断式获取锁

  • 可响应中断式锁可调用方法lock.lockInterruptibly();该方法的底层会调用AQS的acqireInterruptibly方法;

acquireInterruptibly()方法:

public final void acquireInterruptibly(int arg)
       throws InterruptedException {
   //这里会先检查是否有被中断,如果有则抛出一个中断异常
   //否则尝试去获取同步状态,成功直接退出,失败则进入doAcquireInterruptibly(arg)方法
   if (Thread.interrupted())
       throw new InterruptedException();
   if (!tryAcquire(arg))
       doAcquireInterruptibly(arg);
}

doAcquireInterruptibly()方法:

  • 如果当前线程的前驱节点为队头时,尝试获取同步状态
    若获取成功则将队头节点出队,当前线程置为持有锁线程,并将队头指针指向当前线程所封装的节点,否则不断自旋直到获取成功或者线程被中断

/**
* Acquires in exclusive interruptible mode.
* @param arg the acquire argument
*/
private void doAcquireInterruptibly(int arg)
   throws InterruptedException {
       //尝试将节点插入到同步队列
   final Node node = addWaiter(Node.EXCLUSIVE);
   boolean failed = true;
   try {
       for (;;) {
           //取当前节点的前驱节点
           final Node p = node.predecessor();
           //如果前驱节点为头结点,且成功获取同步状态则将持有锁线程
           //置为当前线程,并将队头节点出队
           if (p == head && tryAcquire(arg)) {
               setHead(node);
               p.next = null; // help GC
               failed = false;
               return;
           }
           //获取同步状态失败,线程进入等待状态等待获取独占式
           if (shouldParkAfterFailedAcquire(p, node) &&
               parkAndCheckInterrupt())
               //线程被阻塞时若检测到中断抛出中断异常后退出。
               throw new InterruptedException();
       }
   } finally {
       //获取同步状态失败将当前节点取消
       if (failed)
           cancelAcquire(node);
   }
}

超时等待获取锁(tryAcquireNanos()方法)

  • 调用lock.tryLock(timeout,TimeUnit)方式

  • boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

  • 超时等待锁的返回条件:在指定时间内获取到锁在指定时间内没有获取到锁在指定时间内被中断

public boolean tryLock(long timeout, TimeUnit unit)
       throws InterruptedException {
   return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
  • 该方法调用AQS的tryAcquireNanos()方法:先判断是否被中断,然后尝试获取同步状态
    如果成功则直接返会,否则进入到doAcquireNanos()方法:

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
       throws InterruptedException {
   if (Thread.interrupted())
       throw new InterruptedException();
   return tryAcquire(arg) ||
       doAcquireNanos(arg, nanosTimeout);
}
5ae

doAcquireNanos()方法:

/**
* Acquires in exclusive timed mode.
*
* @param arg the acquire argument
* @param nanosTimeout max wait time
* @return {@code true} if acquired
*/
private boolean doAcquireNanos(int arg, long nanosTimeout)
       throws InterruptedException {
   //如果设置的时间不合法,直接返回
   if (nanosTimeout <= 0L)
       return false;
   //计算截止时间
   final long deadline = System.nanoTime() + nanosTimeout;
   //尝试将当前节点插入到同步队列
   final Node node = addWaiter(Node.EXCLUSIVE);
   boolean failed = true;
   try {
       for (;;) {
           final Node p = node.predecessor();
           //当前线程获得锁出队
           if (p == head && tryAcquire(arg)) {
               setHead(node);
               p.next = null; // help GC
               failed = false;
               return true;
           }
           //重新计算超时时间
           nanosTimeout = deadline - System.nanoTime();
           //如果超时直接返回
           if (nanosTimeout <= 0L)
               return false;
           //线程阻塞等待
           if (shouldParkAfterFailedAcquire(p, node) &&
               nanosTimeout > spinForTimeoutThreshold)
               LockSupport.parkNanos(this, nanosTimeout);
           //若线程被中断则抛出中断异常
           if (Thread.interrupted())
               throw new InterruptedException();
       }
   } finally {
       if (failed)
           cancelAcquire(node);
   }
}

最后

感谢大家看到这里,文章有不足,欢迎大家指出;如果你觉得写得不错,那就给我一个赞吧。

也欢迎大家关注我的公众号:程序员麦冬,麦冬每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!


标签: