Java并发编程(十三):ReentrantLock-tryLock(long timeout, TimeUnit unit)源码分析

Posted 黄智霖-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java并发编程(十三):ReentrantLock-tryLock(long timeout, TimeUnit unit)源码分析相关的知识,希望对你有一定的参考价值。

  在前文ReentrantLock-NonfairSync源码逐行深度分析中,已经分析了AQS加解锁,阻塞唤醒与CLH队列的使用,这里主要看看带超时时间的tryLock实现。

  在ReentrantLock的tryLock(timeout)方法中调用的是sync的tryAcquireNanos:

public boolean tryLock(long timeout, TimeUnit unit)
            throws InterruptedException 
        return sync.tryAcquireNanos(1, unit.toNanos(timeout));
    

  我们知道Sync是ReentrantLock的一个静态内部类,继承自AQS,这个tryAcquireNanos方法实际实现在父类AQS中:

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

  和lockInterruptibly方法的逻辑类似,首先尝试获取锁,获取锁失败则需要入队阻塞(具体逻辑参考前面文章)。只是这里调用的是doAcquireNanos方法,来看看这个方法的实现:

    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)
                    //阻塞时间大于阈值才会park线程
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            
         finally 
            if (failed)
                cancelAcquire(node);
        
    

  主体逻辑和doAcquireInterruptibly方法差不多,如果是首个排队线程,则需要再次尝试获取锁,失败则需要入队阻塞,中断也会抛出异常,然后在cancelAcquire方法中“移除”节点。但是这里多了等待时间的逻辑,实现也很简单:在阻塞的时候使用了parkNanos方法,传入了超时时间nanosTimeout,nanosTimeout每次自旋时由最大等待截止时间减去当前时间得到。
  不过这里需要注意一个spinForTimeoutThreshold参数,当计算得到的nanosTimeout大于该值才会park线程。这个阈值存在的意义是,如果剩余等待的时间很短,那么就不需要陷入park中,默认为1000:

static final long spinForTimeoutThreshold = 1000L;

以上是关于Java并发编程(十三):ReentrantLock-tryLock(long timeout, TimeUnit unit)源码分析的主要内容,如果未能解决你的问题,请参考以下文章

《Java并发编程的艺术》读后笔记-第五章 Java中的锁

Java并发编程原理与实战四十三:CAS ---- ABA问题

Java并发编程原理与实战十三:JDK提供的原子类原理与使用

Java并发编程(十三)在现有的线程安全类中添加功能

转: Java并发编程之十三:生产者—消费者模型(含代码)

Java并发编程(十三):ReentrantLock-tryLock(long timeout, TimeUnit unit)源码分析