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

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

       lock操作

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

[java] view plain copy
  1. /** 
  2.   *默認處理中斷方式是selfInterrupt 
  3.  */  
  4. public final void acquire(int arg) {  
  5.     if (!tryAcquire(arg) &&  
  6.         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  
  7.         selfInterrupt();  
  8. }  
      acquireQueued,在for循環中無條件重試獲取鎖,直到成功獲取鎖,同時返回線程中斷狀態。該方法通過for循正常返回時,必定是成功獲取到了鎖。

[java] view plain copy
  1. /** 
  2.  *無條件重試,直到成功返回,並且記錄中斷狀態 
  3.  */  
  4. final boolean acquireQueued(final Node node, int arg) {  
  5.     boolean failed = true;  
  6.     try {  
  7.         boolean interrupted = false;  
  8.         for (;;) {  
  9.             final Node p = node.predecessor();  
  10.             if (p == head && tryAcquire(arg)) {  
  11.                 setHead(node);  
  12.                 p.next = null// help GC  
  13.                 failed = false;  
  14.                 return interrupted;  
  15.             }  
  16.             if (shouldParkAfterFailedAcquire(p, node) &&  
  17.                 parkAndCheckInterrupt())  
  18.                 interrupted = true;  
  19.         }  
  20.     } finally {  
  21.         if (failed)  
  22.             cancelAcquire(node);  
  23.     }  
  24. }  

    lockInterruptibly操作

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

[java] view plain copy
  1. /** 
  2.      * Acquires in exclusive interruptible mode. 
  3.      * @param arg the acquire argument 
  4.      */  
  5.     private void doAcquireInterruptibly(int arg)  
  6.         throws InterruptedException {  
  7.         final Node node = addWaiter(Node.EXCLUSIVE);  
  8.         boolean failed = true;  
  9.         try {  
  10.             for (;;) {  
  11.                 final Node p = node.predecessor();  
  12.                 if (p == head && tryAcquire(arg)) {  
  13.                     setHead(node);  
  14.                     p.next = null// help GC  
  15.                     failed = false;  
  16.                     return;  
  17.                 }  
  18.                 if (shouldParkAfterFailedAcquire(p, node) &&  
  19.                     parkAndCheckInterrupt())  
  20.                     throw new InterruptedException();  
  21.             }  
  22.         } finally {  
  23.             if (failed)  
  24.                 cancelAcquire(node);  
  25.         }  
  26.     }  

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章