公平模式ReentrantLock實現原理html
前面的文章研究了AbstractQueuedSynchronizer的獨佔鎖和共享鎖,有了前兩篇文章的基礎,就能夠乘勝追擊,看一下基於AbstractQueuedSynchronizer的併發類是如何實現的。java
ReentrantLock顯然是一種獨佔鎖,首先是公平模式的ReentrantLock,Sync是ReentractLock中的基礎類,繼承自AbstractQueuedSynchronizer,看一下代碼實現:算法
1 abstract static class Sync extends AbstractQueuedSynchronizer { 2 private static final long serialVersionUID = -5179523762034025860L; 3 4 /** 5 * Performs {@link Lock#lock}. The main reason for subclassing 6 * is to allow fast path for nonfair version. 7 */ 8 abstract void lock(); 9 10 /** 11 * Performs non-fair tryLock. tryAcquire is 12 * implemented in subclasses, but both need nonfair 13 * try for trylock method. 14 */ 15 final boolean nonfairTryAcquire(int acquires) { 16 final Thread current = Thread.currentThread(); 17 int c = getState(); 18 if (c == 0) { 19 if (compareAndSetState(0, acquires)) { 20 setExclusiveOwnerThread(current); 21 return true; 22 } 23 } 24 else if (current == getExclusiveOwnerThread()) { 25 int nextc = c + acquires; 26 if (nextc < 0) // overflow 27 throw new Error("Maximum lock count exceeded"); 28 setState(nextc); 29 return true; 30 } 31 return false; 32 } 33 34 protected final boolean tryRelease(int releases) { 35 int c = getState() - releases; 36 if (Thread.currentThread() != getExclusiveOwnerThread()) 37 throw new IllegalMonitorStateException(); 38 boolean free = false; 39 if (c == 0) { 40 free = true; 41 setExclusiveOwnerThread(null); 42 } 43 setState(c); 44 return free; 45 } 46 47 protected final boolean isHeldExclusively() { 48 // While we must in general read state before owner, 49 // we don't need to do so to check if current thread is owner 50 return getExclusiveOwnerThread() == Thread.currentThread(); 51 } 52 53 final ConditionObject newCondition() { 54 return new ConditionObject(); 55 } 56 57 // Methods relayed from outer class 58 59 final Thread getOwner() { 60 return getState() == 0 ? null : getExclusiveOwnerThread(); 61 } 62 63 final int getHoldCount() { 64 return isHeldExclusively() ? getState() : 0; 65 } 66 67 final boolean isLocked() { 68 return getState() != 0; 69 } 70 71 /** 72 * Reconstitutes this lock instance from a stream. 73 * @param s the stream 74 */ 75 private void readObject(java.io.ObjectInputStream s) 76 throws java.io.IOException, ClassNotFoundException { 77 s.defaultReadObject(); 78 setState(0); // reset to unlocked state 79 } 80 }
Sync屬於一個公共類,它是抽象的說明Sync會被繼承,簡單整理一下Sync主要作了哪些事(由於Sync不是ReentrantLock公平鎖的關鍵):多線程
接着,看一下公平鎖的實現,FairSync類,它繼承自Sync:併發
1 static final class FairSync extends Sync { 2 private static final long serialVersionUID = -3000897897090466540L; 3 4 final void lock() { 5 acquire(1); 6 } 7 8 /** 9 * Fair version of tryAcquire. Don't grant access unless 10 * recursive call or no waiters or is first. 11 */ 12 protected final boolean tryAcquire(int acquires) { 13 final Thread current = Thread.currentThread(); 14 int c = getState(); 15 if (c == 0) { 16 if (!hasQueuedPredecessors() && 17 compareAndSetState(0, acquires)) { 18 setExclusiveOwnerThread(current); 19 return true; 20 } 21 } 22 else if (current == getExclusiveOwnerThread()) { 23 int nextc = c + acquires; 24 if (nextc < 0) 25 throw new Error("Maximum lock count exceeded"); 26 setState(nextc); 27 return true; 28 } 29 return false; 30 } 31 }
整理一下要點:less
非公平模式ReentrantLock實現原理性能
看完了公平模式ReentrantLock,接着咱們看一下非公平模式ReentrantLock是如何實現的。NonfairSync類,一樣是繼承自Sync類,實現爲:ui
1 static final class NonfairSync extends Sync { 2 private static final long serialVersionUID = 7316153563782823691L; 3 4 /** 5 * Performs lock. Try immediate barge, backing up to normal 6 * acquire on failure. 7 */ 8 final void lock() { 9 if (compareAndSetState(0, 1)) 10 setExclusiveOwnerThread(Thread.currentThread()); 11 else 12 acquire(1); 13 } 14 15 protected final boolean tryAcquire(int acquires) { 16 return nonfairTryAcquire(acquires); 17 } 18 }
結合nonfairTryAcquire方法一塊兒講解,nonfairTryAcquire方法的實現爲:this
1 final boolean nonfairTryAcquire(int acquires) { 2 final Thread current = Thread.currentThread(); 3 int c = getState(); 4 if (c == 0) { 5 if (compareAndSetState(0, acquires)) { 6 setExclusiveOwnerThread(current); 7 return true; 8 } 9 } 10 else if (current == getExclusiveOwnerThread()) { 11 int nextc = c + acquires; 12 if (nextc < 0) // overflow 13 throw new Error("Maximum lock count exceeded"); 14 setState(nextc); 15 return true; 16 } 17 return false; 18 }
看到差異就在於非公平鎖lock()的時候會先嚐試經過CAS看看能不能把state從0變爲1(即獲取鎖),若是能夠的話,直接獲取鎖而不須要排隊。舉個實際例子就很好理解了:spa
看到整個過程當中,後來的線程4反而比先來的線程2先獲取鎖,至關因而一種非公平的模式,
那爲何非公平鎖效率會比公平鎖效率高?上面第(3)步若是線程2和線程4不競爭鎖就是答案。爲何這麼說,後面的解釋很重要,但願你們能夠理解:
線程1是先將state設爲0,再去喚醒線程2,這兩個過程之間是有時間差的。
那麼若是線程1將state設置爲0的時候,線程4就經過CAS算法獲取到了鎖,且在線程1喚醒線程2以前就已經使用完畢鎖,那麼至關於線程2獲取鎖的時間並無推遲,在線程1將state設置爲0到線程1喚醒線程2的這段時間裏,反而有線程4獲取了鎖執行了任務,這就增長了系統的吞吐量,至關於單位時間處理了更多的任務。
從這段解釋咱們也應該能看出來了,非公平鎖比較適合加鎖時間比較短的任務。這是由於加鎖時間長,至關於線程1將state設爲0並去喚醒線程2的這段時間,線程4沒法完成釋放鎖,那麼線程2被喚醒因爲無法獲取到鎖,又被阻塞了,這種喚醒-阻塞的操做會引發線程的上下文切換,繼而影響系統的性能。
Semaphore實現原理
Semaphore即信號量,用於控制代碼塊的併發數,將Semaphore的permits設置爲1至關於就是synchronized或者ReentrantLock,Semaphore具體用法可見Java多線程19:多線程下的其餘組件之CountDownLatch、Semaphore、Exchanger。信號量容許多條線程獲取鎖,顯然它的鎖是一種共享鎖,信號量也有公平模式與非公平模式,相信看懂了上面ReentrantLock的公平模式與非公平模式的朋友應該對Semaphore的公平模式與非公平模式理解起來會更快,這裏就放在一塊兒寫了。
首先仍是看一下Semaphore的基礎設施,它和ReentrantLock同樣,也有一個Sync:
1 abstract static class Sync extends AbstractQueuedSynchronizer { 2 private static final long serialVersionUID = 1192457210091910933L; 3 4 Sync(int permits) { 5 setState(permits); 6 } 7 8 final int getPermits() { 9 return getState(); 10 } 11 12 final int nonfairTryAcquireShared(int acquires) { 13 for (;;) { 14 int available = getState(); 15 int remaining = available - acquires; 16 if (remaining < 0 || 17 compareAndSetState(available, remaining)) 18 return remaining; 19 } 20 } 21 22 protected final boolean tryReleaseShared(int releases) { 23 for (;;) { 24 int current = getState(); 25 int next = current + releases; 26 if (next < current) // overflow 27 throw new Error("Maximum permit count exceeded"); 28 if (compareAndSetState(current, next)) 29 return true; 30 } 31 } 32 33 final void reducePermits(int reductions) { 34 for (;;) { 35 int current = getState(); 36 int next = current - reductions; 37 if (next > current) // underflow 38 throw new Error("Permit count underflow"); 39 if (compareAndSetState(current, next)) 40 return; 41 } 42 } 43 44 final int drainPermits() { 45 for (;;) { 46 int current = getState(); 47 if (current == 0 || compareAndSetState(current, 0)) 48 return current; 49 } 50 } 51 }
和ReentrantLock的Sync差很少,Semaphore的Sync定義瞭如下的一些主要內容:
再看下公平信號量的實現,一樣的FairSync,繼承自Sync,代碼爲:
1 static final class FairSync extends Sync { 2 private static final long serialVersionUID = 2014338818796000944L; 3 4 FairSync(int permits) { 5 super(permits); 6 } 7 8 protected int tryAcquireShared(int acquires) { 9 for (;;) { 10 if (hasQueuedPredecessors()) 11 return -1; 12 int available = getState(); 13 int remaining = available - acquires; 14 if (remaining < 0 || 15 compareAndSetState(available, remaining)) 16 return remaining; 17 } 18 } 19 }
首先第10行的hasQueuedPredecessors方法,前面已經說過了,若是已經有了FIFO隊列或者當前線程不是FIFO隊列中在等待的第一條線程,返回-1,表示沒法獲取共享鎖成功。
接着獲取available,available就是state,用volatile修飾,因此線程中能夠看到最新的state,信號量的acquires是1,每次獲取信號量都對state-1,兩種狀況直接返回:
以後就是和以前說過的共享鎖的邏輯了,若是返回的是一個<0的數字,那麼構建FIFO隊列,線程阻塞,直到前面的執行完才能喚醒後面的。
接着看一下非公平信號量的實現,NonfairSync繼承Sync:
1 static final class NonfairSync extends Sync { 2 private static final long serialVersionUID = -2694183684443567898L; 3 4 NonfairSync(int permits) { 5 super(permits); 6 } 7 8 protected int tryAcquireShared(int acquires) { 9 return nonfairTryAcquireShared(acquires); 10 } 11 }
nonfairTryAcquireShared在父類已經實現了,再貼一下代碼:
1 final int nonfairTryAcquireShared(int acquires) { 2 for (;;) { 3 int available = getState(); 4 int remaining = available - acquires; 5 if (remaining < 0 || 6 compareAndSetState(available, remaining)) 7 return remaining; 8 } 9 }
看到這裏和公平Semaphore只有一點差異:不會前置進行一次hasQueuedPredecessors()判斷。即當前有沒有構建爲一個FIFO隊列,隊列裏面第一個等待的線程是否是自身都無所謂,對於非公平Semaphore都同樣,反正線程調用Semaphore的acquire方法就將當前state-1,若是獲得的remaining設置成功或者CAS操做成功就返回,這種操做沒有遵循先到先得的原則,即非公平信號量。
至於非公平信號量對比公平信號量的優勢,和ReentrantLock的非公平鎖對比ReentrantLock的公平鎖同樣,就不說了。
CountDownLatch實現原理
CountDownLatch即計數器自減的一種閉鎖,某線程阻塞,對一個計數器自減到0,此線程被喚醒,CountDownLatch具體用法可見Java多線程19:多線程下的其餘組件之CountDownLatch、Semaphore、Exchanger。
CountDownLatch是一種共享鎖,經過await()方法與countDown()兩個方法實現自身的功能,首先看一下await()方法的實現:
1 public void await() throws InterruptedException { 2 sync.acquireSharedInterruptibly(1); 3 }
acquireSharedInterruptibly最終又回到tryAcquireShared方法上,直接貼整個Sync的代碼實現:
1 private static final class Sync extends AbstractQueuedSynchronizer { 2 private static final long serialVersionUID = 4982264981922014374L; 3 4 Sync(int count) { 5 setState(count); 6 } 7 8 int getCount() { 9 return getState(); 10 } 11 12 protected int tryAcquireShared(int acquires) { 13 return (getState() == 0) ? 1 : -1; 14 } 15 16 protected boolean tryReleaseShared(int releases) { 17 // Decrement count; signal when transition to zero 18 for (;;) { 19 int c = getState(); 20 if (c == 0) 21 return false; 22 int nextc = c-1; 23 if (compareAndSetState(c, nextc)) 24 return nextc == 0; 25 } 26 } 27 }
其實看到tryAcquireShared方法,理解AbstractQueuedSynchronizer共享鎖原理的,不用看countDown方法應該都能猜countDown方法是如何實現的。我這裏總結一下:
爲了驗證(2),看一下上面Sync的tryReleaseShared方法就能夠了,確實是這麼實現的。
再理解獨佔鎖與共享鎖的區別
本文詳細分析了ReentrantLock、Semaphore、CountDownLatch的實現原理,第一個是基於獨佔鎖的實現,後兩個是基於共享鎖的實現,從這三個類咱們能夠再總結一下獨佔鎖與共享鎖的區別,主要在兩點上:
帶着這兩個結論再看ReentrantLock、Semaphore、CountDownLatch,你必定會對獨佔鎖與共享鎖理解更深。