java可中断式获取锁与超时等待获取锁

java的Lock体系相比synchronize具有一些更方便的特性,比如锁可以响应中断以及超时等待等特性,我们可以通过看源码来看看响应中断是如何实现的。

一、可中断式获取锁

可响应中断式锁调用的方法是lock.lockInterruptibly()

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

而lockInterruptibly()会调用AQS的acquireInterruptibly()模板方法

    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        //增加了对中断状态的判断,
        //如果检测线程中断状态改变,抛出中断异常后方法直接退出 
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            //线程获取锁失败
            doAcquireInterruptibly(arg);
    }

在获取同步状态失败后就会调用doAcquireInterruptibly方法:

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

这段代码以中断模式独占获取同步状态,与acquire方法逻辑几乎一致,唯一的区别是当parkAndCheckInterrupt返回true时即线程阻塞时该线程被中断,代码抛出被中断异常。

二、超时等待获取锁

超时等待获取锁就是在中断获取锁的基础上增加超时功能

调用lock.tryLock(timeout,TimeUnit)方法实现超时等待获取锁的效果,该方法会在三种情况下才会返回:

  • 在超时时间内,当前线程成功获取了锁;
  • 当前线程在超时时间内被中断;
  • 超时时间结束,仍未获得锁返回false

tryLock(timeout,TimeUnit)源码

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

该方法本质调用AQS的模板方法public final boolean tryAcquireNanos(int arg, long nanosTimeout)

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

最终调用doAcquireNanos()方法实现超时等待效果

private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        // 传入时间小于0 方法直接退出,线程获取锁失败
        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可中断式获取锁与超时等待获取锁》

    原文作者:java锁
    原文地址: https://blog.csdn.net/eve8888888/article/details/84644583
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞