java並發-ReentrantLock的lock和lockInterruptibly的區別


ReentrantLock的加鎖方法Lock()提供了無條件地輪詢獲取鎖的方式,lockInterruptibly()提供了可中斷的鎖獲取方式。這兩個方法的區別在哪里呢?通過分析源碼可以知道lock方法默認處理了中斷請求,一旦監測到中斷狀態,則中斷當前線程;而lockInterruptibly()則直接拋出中斷異常,由上層調用者區去處理中斷。

1  lock操作

         lock獲取鎖過程中,忽略了中斷,在成功獲取鎖之后,再根據中斷標識處理中斷,即selfInterrupt中斷自己。 acquire操作源碼如下:

/**

  *默認處理中斷方式是selfInterrupt

 */  

public final void acquire(int arg) {  

if (!tryAcquire(arg) &&  

        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  

        selfInterrupt();  

}  

      acquireQueued,在for循環中無條件重試獲取鎖,直到成功獲取鎖,同時返回線程中斷狀態。該方法通過for循正常返回時,必定是成功獲取到了鎖。

/**

 *無條件重試,直到成功返回,並且記錄中斷狀態

 */  

final boolean acquireQueued(final Node node, int arg) {  

boolean failed = true;  

try {  

boolean interrupted = false;  

for (;;) {  

final Node p = node.predecessor();  

if (p == head && tryAcquire(arg)) {  

                setHead(node);  

p.next =null; // help GC  

failed =false;  

return interrupted;  

            }  

if (shouldParkAfterFailedAcquire(p, node) &&  

                parkAndCheckInterrupt())  

interrupted =true;  

        }  

}finally {  

if (failed)  

            cancelAcquire(node);  

    }  

}  

2 lockInterruptibly操作

     可中斷加鎖,即在鎖獲取過程中不處理中斷狀態,而是直接拋出中斷異常,由上層調用者處理中斷。源碼細微差別在於鎖獲取這部分代碼,這個方法與acquireQueue差別在於方法的返回途徑有兩種,一種是for循環結束,正常獲取到鎖;另一種是線程被喚醒后檢測到中斷請求,則立即拋出中斷異常,該操作導致方法結束。

/**

     * Acquires in exclusive interruptible mode.

     * @param arg the acquire argument

     */  

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

        }  

    }  

     結論:ReentrantLock的中斷和非中斷加鎖模式的區別在於:線程嘗試獲取鎖操作失敗后,在等待過程中,如果該線程被其他線程中斷了,它是如何響應中斷請求的。lock方法會忽略中斷請求,繼續獲取鎖直到成功;而lockInterruptibly則直接拋出中斷異常來立即響應中斷,由上層調用者處理中斷。

     那么,為什么要分為這兩種模式呢?這兩種加鎖方式分別適用於什么場合呢?根據它們的實現語義來理解,我認為lock()適用於鎖獲取操作不受中斷影響的情況,此時可以忽略中斷請求正常執行加鎖操作,因為該操作僅僅記錄了中斷狀態(通過Thread.currentThread().interrupt()操作,只是恢復了中斷狀態為true,並沒有對中斷進行響應)。如果要求被中斷線程不能參與鎖的競爭操作,則此時應該使用lockInterruptibly方法,一旦檢測到中斷請求,立即返回不再參與鎖的競爭並且取消鎖獲取操作(即finally中的cancelAcquire操作)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM