天天看點

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 的線程一旦被打斷,直接抛異常。

繼續閱讀