ReentrantLock的加鎖方法Lock()提供了無條件地輪詢獲取鎖的方式,lockInterruptibly()提供了可中斷的鎖獲取方式。這兩個方法的區別在哪裏呢?通過分析源碼可以知道lock方法默認處理了中斷請求,一旦監測到中斷狀態,則中斷當前線程;而lockInterruptibly()則直接拋出中斷異常,由上層調用者區去處理中斷。
lock操作
lock獲取鎖過程中,忽略了中斷,在成功獲取鎖之後,再根據中斷標識處理中斷,即selfInterrupt中斷自己。 acquire操作源碼如下:
- /**
- *默認處理中斷方式是selfInterrupt
- */
- public final void acquire(int arg) {
- if (!tryAcquire(arg) &&
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
- selfInterrupt();
- }
- /**
- *無條件重試,直到成功返回,並且記錄中斷狀態
- */
- 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);
- }
- }
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操作)。