Jdk1.6 JUC原始碼解析(7)-locks-ReentrantLock

壹頁書發表於2015-11-13
轉自 大飛的部落格
http://brokendreams.iteye.com/blog/2250883

Jdk1.6 JUC原始碼解析(7)-locks-ReentrantLock

作者:大飛


功能簡介:
  • Java程式碼層面提供的鎖機制,可做為Synchronized(jvm內建)的替代物,和Synchronized一樣都是可重入的。
  • 與Synchronized相比較而言,ReentrantLock有以下優勢:支援公平/非公平鎖、支援可中斷的鎖、支援非阻塞的tryLock(可超時)、支援鎖條件、可跨程式碼塊使用(一個地方加鎖,另一個地方解鎖),總之比Synchronized更加靈活。但也有缺點,比如鎖需要顯示解鎖、無法充分享用JVM內部效能提升帶來的好處等等。
原始碼分析:
  • ReentrantLock實現了Lock介面,先來看下這個介面:
Java程式碼  
  1. public interface Lock {  
  2.     /** 
  3.      * 獲取鎖,如果鎖無法獲取,當前執行緒被阻塞,直到鎖可以獲取並獲取成功為止。 
  4.      */  
  5.     void lock();  
  6.     /** 
  7.      * 在當前執行緒沒有被中斷的情況下獲取鎖。 
  8.      * 
  9.      * 如果獲取成功,方法結束。 
  10.      * 
  11.      * 如果鎖無法獲取,當前執行緒被阻塞,直到下面情況發生: 
  12.      * 
  13.      * 1.當前執行緒(被喚醒後)成功獲取鎖。 
  14.      * 2.當前執行緒被其他執行緒中斷。 
  15.      */  
  16.     void lockInterruptibly() throws InterruptedException;  
  17.     /** 
  18.      * 如果當前鎖是可用的,獲取鎖。 
  19.      * 
  20.      * 獲取成功後,返回true。 
  21.      * 如果當前鎖不可用,返回false。 
  22.      */  
  23.     boolean tryLock();  
  24.     /** 
  25.      * 如果鎖在給定超時時間內可用,並且當前執行緒沒有被中斷,那麼獲取鎖。 
  26.      * 
  27.      * 如果鎖可用,獲取鎖成功並返回true。 
  28.      *  
  29.      * 如果鎖無法獲取,當前執行緒被阻塞,直到下面情況發生: 
  30.      * 
  31.      * 1.當前執行緒(被喚醒後)成功獲取鎖。 
  32.      * 2.當前執行緒被其他執行緒中斷。 
  33.      * 3.指定的等待時間超時。 
  34.      */  
  35.     boolean tryLock(long time, TimeUnit unit) throws InterruptedException;  
  36.     /** 
  37.      * 釋放鎖。 
  38.      */  
  39.     void unlock();  
  40.     /** 
  41.      * 返回一個和當前鎖例項相關聯的條件。 
  42.      * 
  43.      * 當前執行緒必須首先獲取鎖後才能在鎖條件上等待。 
  44.      * 一個Condition的await()方法呼叫會在等待之前自動釋放鎖,在等待結束 
  45.      * 前重新獲取鎖。 
  46.      */  
  47.     Condition newCondition();  
  48. }  



  • 之前分析AQS的時候提到過,基於AQS構建的同步機制都會使用內部幫助類繼承AQS的方式構建,看下ReentrantLock中的同步機制:
Java程式碼  
  1. //內部同步機制的引用。  
  2. private final Sync sync;  
  3. /** 
  4.  * 這個鎖實現的基本同步控制機制,下面會提供公平和非公平版本的子類。 
  5.  * 利用AQS的state來表示鎖持有(重入)的次數。. 
  6.  */  
  7. static abstract class Sync extends AbstractQueuedSynchronizer {  
  8.     private static final long serialVersionUID = -5179523762034025860L;  
  9.     /** 
  10.      * Performs {@link Lock#lock}. The main reason for subclassing 
  11.      * is to allow fast path for nonfair version. 
  12.      */  
  13.     abstract void lock();  
  14.     /** 
  15.      * 方法用來支援非公平的tryLock  
  16.      */  
  17.     final boolean nonfairTryAcquire(int acquires) {  
  18.         final Thread current = Thread.currentThread();  
  19.         int c = getState();  
  20.         if (c == 0) {  
  21.             //如果當前沒有任何執行緒獲取鎖(鎖可用),嘗試設定state。  
  22.             if (compareAndSetState(0, acquires)) {  
  23.                 //如果設定成功,將當前執行緒資訊設定到AQS中(所有權關聯)。   
  24.                 setExclusiveOwnerThread(current);  
  25.                 return true;  
  26.             }  
  27.         }  
  28.         //如果鎖已經被持有,那麼判斷一下持有鎖的執行緒是否為當前執行緒。  
  29.         else if (current == getExclusiveOwnerThread()) {  
  30.             //如果是當前執行緒在持有鎖,那麼這裡累計一下重入次數。  
  31.             int nextc = c + acquires;  
  32.             if (nextc < 0// overflow  重入次數最大不能超過int的最大值  
  33.                 throw new Error("Maximum lock count exceeded");  
  34.             //設定到AQS的state中  
  35.             setState(nextc);  
  36.             return true;  
  37.         }  
  38.         //如果鎖已經被持有,且持有執行緒不是當前執行緒,返回false。  
  39.         return false;  
  40.     }  
  41.     protected final boolean tryRelease(int releases) {  
  42.         //釋放時,這裡要減去重入次數。  
  43.         int c = getState() - releases;  
  44.         //判斷控制權關係是否正確。  
  45.         if (Thread.currentThread() != getExclusiveOwnerThread())  
  46.             throw new IllegalMonitorStateException();  
  47.         boolean free = false;  
  48.         if (c == 0) {  
  49.             //如果當前執行緒完全釋放了鎖(重入次數為0)  
  50.             free = true;  
  51.             //解除所有權關係。  
  52.             setExclusiveOwnerThread(null);  
  53.         }  
  54.         //設定重入次數。  
  55.         setState(c);  
  56.         //返回是否釋放成功(或者說是否完全釋放)。  
  57.         return free;  
  58.     }  
  59.     protected final boolean isHeldExclusively() {  
  60.         // While we must in general read state before owner,  
  61.         // we don't need to do so to check if current thread is owner  
  62.         return getExclusiveOwnerThread() == Thread.currentThread();  
  63.     }  
  64.     final ConditionObject newCondition() {  
  65.         return new ConditionObject();  
  66.     }  
  67.     // Methods relayed from outer class  
  68.     final Thread getOwner() {  
  69.         return getState() == 0 ? null : getExclusiveOwnerThread();  
  70.     }  
  71.     final int getHoldCount() {  
  72.         return isHeldExclusively() ? getState() : 0;  
  73.     }  
  74.     final boolean isLocked() {  
  75.         return getState() != 0;  
  76.     }  
  77.     /** 
  78.      * Reconstitutes this lock instance from a stream. 
  79.      * @param s the stream 
  80.      */  
  81.     private void readObject(java.io.ObjectInputStream s)  
  82.         throws java.io.IOException, ClassNotFoundException {  
  83.         s.defaultReadObject();  
  84.         setState(0); // reset to unlocked state  
  85.     }  
  86. }  


       接下來先看一下非公平版本的子類:

Java程式碼  
  1. /** 
  2.  * Sync object for non-fair locks 
  3.  */  
  4. final static class NonfairSync extends Sync {  
  5.     private static final long serialVersionUID = 7316153563782823691L;  
  6.     /** 
  7.      * Performs lock.  Try immediate barge, backing up to normal 
  8.      * acquire on failure. 
  9.      */  
  10.     final void lock() {  
  11.         //這裡首先嚐試一個短程式碼路徑,直接CAS設定state,嘗試獲取鎖。  
  12.         //相當於一個插隊的動作(可能出現AQS等待佇列裡有執行緒在等待,但當前執行緒競爭成功)。  
  13.         if (compareAndSetState(01))  
  14.             setExclusiveOwnerThread(Thread.currentThread());  
  15.         else   
  16.             acquire(1);//如果CAS失敗,呼叫AQS的獨佔請求方法。  
  17.     }  
  18.     protected final boolean tryAcquire(int acquires) {  
  19.         //呼叫上面父類的nonfairTryAcquire方法。  
  20.         return nonfairTryAcquire(acquires);  
  21.     }  
  22. }  


       再來先看一下公平版本的子類:

Java程式碼  
  1. /** 
  2.  * Sync object for fair locks 
  3.  */  
  4. final static class FairSync extends Sync {  
  5.     private static final long serialVersionUID = -3000897897090466540L;  
  6.     final void lock() {  
  7.         acquire(1);  
  8.     }  
  9.     /** 
  10.      * 公平版本的tryAcquire。  
  11.      * 只有在遞迴(重入)或者同步佇列中沒有其他執行緒 
  12.      * 或者當前執行緒是等待佇列中的第一個執行緒時才准許訪問。 
  13.      */  
  14.     protected final boolean tryAcquire(int acquires) {  
  15.         final Thread current = Thread.currentThread();  
  16.         int c = getState();  
  17.         if (c == 0) {  
  18.             if (!hasQueuedPredecessors() &&  
  19.                 compareAndSetState(0, acquires)) { //如果當前鎖可用,且同步等待佇列中沒有其他執行緒,那麼嘗試設定state  
  20.                 setExclusiveOwnerThread(current); //如果設定成功,相當於獲取鎖成功,設定所有權關係。  
  21.                 return true;  
  22.             }  
  23.         }  
  24.         else if (current == getExclusiveOwnerThread()) {  
  25.             //如果當前執行緒已經持有該鎖,那麼累計重入次數。  
  26.             int nextc = c + acquires;  
  27.             if (nextc < 0)  
  28.                 throw new Error("Maximum lock count exceeded");  
  29.             setState(nextc);  
  30.             return true;  
  31.         }  
  32.         return false;  
  33.     }  
  34. }  


       小總結一下:
 
       非公平版的鎖-加鎖操作
              1.當前執行緒首先會無條件的執行一個CAS操作來獲取鎖,如果CAS操作成功,獲取鎖成功。
              2.如果第1步沒成功,當前會檢查鎖是否被其他執行緒持有,也就是鎖是否可用。
              3.如果沒有其他執行緒持有鎖,會以CAS的方式嘗試獲取鎖,如果CAS操作成功,獲取鎖成功。
              4.如果有其他執行緒持有鎖,會判斷一下持有鎖的執行緒是否為當前執行緒,如果是當前執行緒,重入次數+1,獲取鎖成功。
              5.根據AQS的分析,上述2、3、4步會執行多次,如果最終獲取鎖失敗,當前執行緒會被阻塞,等待其他執行緒執行解鎖操作將其喚醒。
 
       公平版的鎖-加鎖操作
              1.當前執行緒首先會檢查鎖是否被其他執行緒持有,並且當前同步等待佇列裡有沒有其他執行緒在等待。
              2.如果沒有其他執行緒持有鎖,且同步等待佇列裡沒有其他執行緒,會以CAS的方式嘗試獲取鎖,如果CAS操作成功,獲取鎖成功。
              3.如果有其他執行緒持有鎖,會判斷一下持有鎖的執行緒是否為當前執行緒,如果是當前執行緒,重入次數+1,獲取鎖成功。
              4.根據AQS的分析,上述1、2、3步會執行多次,如果最終獲取鎖失敗,當前執行緒會被阻塞,等待其他執行緒執行解鎖操作將其喚醒。
 
       非公平版和公平版鎖的解鎖操作一樣
              1.當前執行緒首先將鎖重入次數減1(AQS的state),如果減1後結果為0,將當前同步器的執行緒資訊置空,並喚醒同步等待佇列中隊頭的等待執行緒。
              2.如果第1步中,重入次數減1後結果不為0(說明當前執行緒還持有當前鎖),方法結束。
 
  • 有了內部的基礎同步機制,ReentrantLock的實現就很簡單了,直接看程式碼:
Java程式碼  
  1. /** 
  2.  * 預設情況下構建非公平鎖。 
  3.  */  
  4. public ReentrantLock() {  
  5.     sync = new NonfairSync();  
  6. }  
  7. /** 
  8.  * 根據給定的公平策略生成相應的例項。 
  9.  * 
  10.  * @param fair {@code true} if this lock should use a fair ordering policy 
  11.  */  
  12. public ReentrantLock(boolean fair) {  
  13.     sync = (fair)? new FairSync() : new NonfairSync();  
  14. }  
  15.   
  16. public void lock() {  
  17.     sync.lock();  
  18. }  
  19.   
  20. public void lockInterruptibly() throws InterruptedException {  
  21.     sync.acquireInterruptibly(1);  
  22. }  
  23.   
  24. public boolean tryLock() {  
  25.     return sync.nonfairTryAcquire(1);  
  26. }  
  27.   
  28. public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {  
  29.     return sync.tryAcquireNanos(1, unit.toNanos(timeout));  
  30. }  
  31.   
  32. public void unlock() {  
  33.     sync.release(1);  
  34. }  
  35.   
  36. public Condition newCondition() {  
  37.     return sync.newCondition();  
  38. }  


       最後看一下一些支援監測的方法:

Java程式碼  
  1. /** 
  2.  * 獲取當前執行緒的對當前鎖的持有(重入)次數。 
  3.  */  
  4. public int getHoldCount() {  
  5.     return sync.getHoldCount();  
  6. }  
  7. /** 
  8.  * 判斷當前鎖是否被當前執行緒持有。 
  9.  */  
  10. public boolean isHeldByCurrentThread() {  
  11.     return sync.isHeldExclusively();  
  12. }  
  13. /** 
  14.  * 判斷當前鎖是否被(某個執行緒)持有。 
  15.  */  
  16. public boolean isLocked() {  
  17.     return sync.isLocked();  
  18. }  
  19. /** 
  20.  * 當前鎖是否為公平鎖。 
  21.  */  
  22. public final boolean isFair() {  
  23.     return sync instanceof FairSync;  
  24. }  
  25. /** 
  26.  * 獲取持有當前鎖的執行緒。 
  27.  */  
  28. protected Thread getOwner() {  
  29.     return sync.getOwner();  
  30. }  
  31. /** 
  32.  * 判斷是否有執行緒在當前鎖的同步等待佇列中等待。  
  33.  */  
  34. public final boolean hasQueuedThreads() {  
  35.     return sync.hasQueuedThreads();  
  36. }  
  37. /** 
  38.  * 判斷給定的執行緒是否在當前鎖的同步等待佇列中等待。 
  39.  */  
  40. public final boolean hasQueuedThread(Thread thread) {  
  41.     return sync.isQueued(thread);  
  42. }  
  43. /** 
  44.  * 獲取當前鎖的同步等待佇列中的等待執行緒(估計)數量。 
  45.  */  
  46. public final int getQueueLength() {  
  47.     return sync.getQueueLength();  
  48. }  
  49. /** 
  50.  * 獲取當前鎖的同步等待佇列中等待的執行緒。 
  51.  */  
  52. protected Collection getQueuedThreads() {  
  53.     return sync.getQueuedThreads();  
  54. }  
  55. /** 
  56.  * 判斷是否有執行緒在給定條件的條件等待佇列上等待。 
  57.  */  
  58. public boolean hasWaiters(Condition condition) {  
  59.     if (condition == null)  
  60.         throw new NullPointerException();  
  61.     if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))  
  62.         throw new IllegalArgumentException("not owner");  
  63.     return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);  
  64. }  
  65. /** 
  66.  * 獲取給定條件的條件等待佇列中等待執行緒的(估計)數量。 
  67.  */  
  68. public int getWaitQueueLength(Condition condition) {  
  69.     if (condition == null)  
  70.         throw new NullPointerException();  
  71.     if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))  
  72.         throw new IllegalArgumentException("not owner");  
  73.     return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);  
  74. }  
  75. /** 
  76.  * 獲取給定條件的條件等待佇列中等待執行緒。 
  77.  */  
  78. protected Collection getWaitingThreads(Condition condition) {  
  79.     if (condition == null)  
  80.         throw new NullPointerException();  
  81.     if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))  
  82.         throw new IllegalArgumentException("not owner");  
  83.     return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);  
  84. }  


       ReentrantLock的程式碼解析完畢!



       參見:Jdk1.6 JUC原始碼解析(6)-locks-AbstractQueuedSynchronizer


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-1835220/,如需轉載,請註明出處,否則將追究法律責任。

相關文章