源码|并发一枝花之ReentrantLock与AQS(2):lockInterruptibly

上次分析了ReentrantLock#lock()与ReentrantLock#unlock()的实现原理,并初步讨论了AQS的等待队列模型,参考源码|并发一枝花之ReentrantLock与AQS(1):lock、unlockjava

本文继续分析ReentrantLock#lockInterruptibly(),内容简单,能够跳过。node

JDK版本:oracle java 1.8.0_102git

接口声明

public interface Lock {
    ...
    void lock();

    void lockInterruptibly() throws InterruptedException;

    void unlock();
    ...
}
复制代码

Lock#lockInterruptibly()是Lock#lock()的一个衍生品,解锁方法也为Lock#unlock()。所以,咱们只须要分析Lock#lockInterruptibly()。github

功能

Lock#lockInterruptibly()方法比较特殊,当经过这个方法去获取锁时,若是线程正在等待获取锁,则这个线程可以响应中断,即中断线程的等待状态。c#

一个典型用法以下:并发

当两个线程同时经过Lock#lockInterruptibly()阻塞的获取某个锁时,假如此时线程A获取到了锁,则线程B只有继续等待;此时,让线程A对线程B调用threadB.interrupt()方法,可以中断线程B的等待,让线程B能够先作其余事。oracle

想对的,若是使用内置锁synchronized,当一个线程处于等待某个锁的状态时,是没法被中断的,只有一直等待下去。这是可中断锁最大的优点。ui

注意,当一个线程获取了锁以后,是不会被Thread#interrupt()方法中断的。spa

实现原理

基本的实现原理与ReentrantLock#lock()相同。线程

仍以默认的非公平策略为例进行分析。

lockInterruptibly

public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
复制代码

回忆ReentrantLock的核心思想:用state表示“全部者线程已经重复获取该锁的次数”

acquireInterruptibly

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    ...
    public final void acquireInterruptibly(int arg) throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
    ...
}
复制代码

改写:

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    ...
    public final void acquireInterruptibly(int arg) if (Thread.interrupted()) throw new InterruptedException();
        if (tryAcquire(arg)) {
            return;
        }
        doAcquireInterruptibly(arg);
    }
    ...
}
复制代码

首先,经过tryAcquire()尝试获取锁。若是获取成功,则直接返回。若是获取失败,则借助doAcquireInterruptibly实现可中断获取。

NonfairSync#tryAcquire()在ReentrantLock#lock()中分析过了,直接看AQS#doAcquireInterruptibly()。

doAcquireInterruptibly

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
    ...
    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);
        }
    }
    ...
}
复制代码

同AQS#acquireQueued()基本相同,惟一的区别是对中断信号的处理。

AQS#acquireQueued()被中断后,将中断标志传给外界,外界再调用Thread#interrupt()复现中断;而AQS#doAcquireInterruptibly()则直接抛出InterruptedException。

两者本质上没什么不一样。但AQS#doAcquireInterruptibly()显示抛出了InterruptedException,调用者必须处理或继续上抛该异常。

总结

能够看到,分析完ReentrantLock#lock()后,其衍生品ReentrantLock#lockInterruptibly()的分析也变得极其简单。ReentrantLock#tryLock()与之类似,再也不分析。


本文连接:源码|并发一枝花之ReentrantLock与AQS(2):lockInterruptibly
做者:猴子007
出处:monkeysayhi.github.io
本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布,欢迎转载,演绎或用于商业目的,可是必须保留本文的署名及连接。

相关文章
相关标签/搜索