Last analyzes ReentrantLock# lock () and ReentrantLock# unlock () the implementation of the principle, and preliminary discussed the waiting queue model of AQS, reference source | concurrent flower already and AQS (1) : the lock and unlock.

This article continues with ReentrantLock#lockInterruptibly(), which is simple enough to skip.

JDK version: Oracle Java 1.8.0_102

Interface declaration

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

    void lockInterruptibly(a) throws InterruptedException;

    void unlock(a); . }Copy the code

Lock#lockInterruptibly() is a derivative of Lock#lock() and is unlocked by Lock#unlock(). Therefore, we just need to analyze Lock#lockInterruptibly().

function

The Lock#lockInterruptibly() method is special in that it can respond to interrupts, the wait state of the interrupted thread, if the thread is waiting to acquire the lock.

A typical use is as follows:

When two threads simultaneously acquire A lock by blocking Lock#lockInterruptibly(), thread B must wait if thread A acquires the lock. Having thread A call threadb.interrupt () to threadB interrupts threadB’s wait so that threadB can do something else first.

If synchronized is used, a thread cannot be interrupted when it is waiting for a lock. This is the biggest advantage of interruptible locks.

Note that once a thread has acquired the lock, it is not interrupted by the Thread#interrupt() method.

Realize the principle of

The basic implementation principle is the same as ReentrantLock#lock().

Still take the default unfair strategy as an example to analyze.

lockInterruptibly

    public void lockInterruptibly(a) throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
Copy the code

Recall the core idea of ReentrantLock: Use state to indicate “the number of times the owner thread has repeatedly acquired the lock.”

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);
    }
    ...
}
Copy the code

Rewrite:

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {...public final void acquireInterruptibly(int arg)
        if (Thread.interrupted())
            throw new InterruptedException(a);
        if (tryAcquire(arg)) {
            return; } doAcquireInterruptibly(arg); }... }Copy the code

First, try to acquire the lock through tryAcquire(). If successful, it returns directly. If the fetch fails, the doAcquireInterruptibly implementation interrupts the fetch.

NonfairSync#tryAcquire() was analyzed in ReentrantLock#lock(), just look at 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 newInterruptedException(); }}finally {
            if(failed) cancelAcquire(node); }}... }Copy the code

Basically the same as AQS#acquireQueued(), the only difference is the handling of the interrupt signal.

AQS#acquireQueued() passes the interrupt flag to the outside world, which then calls Thread#interrupt() to retrieve the interrupt; AQS#doAcquireInterruptibly() directly throws InterruptedException.

The two are essentially the same. But AQS#doAcquireInterruptibly() shows that InterruptedException was thrown and the caller must handle or continue throwing the exception.

conclusion

As you can see, after analyzing ReentrantLock#lock(), the analysis of its derivative ReentrantLock#lockInterruptibly() becomes extremely simple. ReentrantLock#tryLock() is similar and no longer parsed.


This paper links: source | concurrent flower already and AQS (2) : lockInterruptibly author: monkey 007 reference: Monkeysayhi.github. IO This article is published under the Creative Commons Attribution – Share Alike 4.0 International License. You are welcome to republish, reproduce, or use it for commercial purposes.