AQS打断原理

不可打断模式

平时加锁使用的 reentrantLock.lock() 方法, 默认是不可打断模式,即便 park 状态的线程被打断了,它也不会立即响应,它仍旧在 AQS 中运行着,直到拿到锁,再自己中断自己。

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this); // 线程停在了这里。
    // 获取当前线程是否被打断了,并且清空打断标记。
    return Thread.interrupted();
}

假设 T1 处于 park 状态,但被 T2 拿到了 T1的线程对象,调用 t1.interrupt() 方法,将 t1 从打断中给唤醒了。parkAndCheckInterrupt() 返回 true。

final boolean acquireQueued(final Node node, int arg) {
    // 拿锁失败?默认是。
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) 
            // 如果获得不了锁,T1 依旧会进入 park。
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);  
                p.next = null; 
                failed = false;
                // T1 只有拿到锁时,才能跳出这个死循环。
                // 返回 打断标记,true。
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&            
                parkAndCheckInterrupt())
                // 被打断的 T1 执行到这里。
                interrupted = true;
            	// 死循环,回到上面去重新执行。
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

如果 T1 被打断后,拿到了锁,它返回到哪里了呢

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // acquireQueued() 返回结果true。
        // 执行这个方法。
        selfInterrupt();
}

static void selfInterrupt() {
    // T1自己重新产生一次中断。
    Thread.currentThread().interrupt();
}

可打断模式

调用 reentrantLock.lockInterruptibly() 那就是可打断的。

public void lockInterruptibly() throws InterruptedException {
    // 调用 AQS 的 acquireInterruptibly(1) 方法。
    sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)
    throws InterruptedException {
    // 如果当前线程被打断了,直接抛异常。
    if (Thread.interrupted())
        throw new InterruptedException();
    // 尝试拿锁。
    if (!tryAcquire(arg))
        // 那锁失败,执行该方法。
        doAcquireInterruptibly(arg);
}
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())
                // park 的线程被打断后,直接抛异常。
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

总结

  • reentrantLock.lock() 加锁,线程是不可被打断的,因为即便 T1被打断了,它拿不到锁,从循环里出不来,依旧又会 park。
  • reentrantLock.lockInterruptibly() 加锁,线程是能可被打断的,park 的线程一旦被打断,直接抛异常。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值