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

逻辑图如下

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值