自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java同步機(jī)制的底層實(shí)現(xiàn)

開發(fā) 后端
在多線程編程中我們會(huì)遇到很多需要使用線程同步機(jī)制去解決的并發(fā)問題,而這些同步機(jī)制就是多線程編程中影響正確性和運(yùn)行效率的重中之重。

在多線程編程中我們會(huì)遇到很多需要使用線程同步機(jī)制去解決的并發(fā)問題,而這些同步機(jī)制就是多線程編程中影響正確性和運(yùn)行效率的重中之重。這不禁讓我感到好奇,這些同步機(jī)制是如何實(shí)現(xiàn)的呢?好奇心是進(jìn)步的源泉,就讓我們一起來揭開同步機(jī)制源碼的神秘面紗吧。

在本文中,我們會(huì)從JDK中大多數(shù)同步機(jī)制的共同基礎(chǔ)AbstractQueuedSynchronizer類開始說起,然后通過源碼了解我們最常用的兩個(gè)同步類可重入鎖ReentrantLock和閉鎖CountDownLatch的具體實(shí)現(xiàn)。通過這篇文章我們將可以了解到ReentrantLock和CountDownLatch兩個(gè)常用同步類的源代碼實(shí)現(xiàn),并且掌握閱讀其他基于AQS實(shí)現(xiàn)的同步工具類源碼的能力,甚至可以利用AQS寫出自己的同步工具類。

閱讀這篇文章需要了解基本的線程同步機(jī)制,有興趣的讀者可以參考一下這篇文章《多線程中那些看不到的陷阱》。

同步機(jī)制的核心——AQS

同步機(jī)制源碼初探

ReentrantLock是我們常用的一種可重入互斥鎖,是synchronized關(guān)鍵字的一個(gè)很好的替代品?;コ庵傅木褪峭粫r(shí)間只能有一個(gè)線程獲取到這個(gè)鎖,而可重入是指如果一個(gè)線程再次獲取一個(gè)它已經(jīng)持有的互斥鎖,那么仍然會(huì)成功。

這個(gè)類的源碼在JDK的java.util.concurrent包下,我們可以在IDE中點(diǎn)擊類名跳轉(zhuǎn)到具體的類定義,比如下面就是在我的電腦上跳轉(zhuǎn)之后看到的ReentrantLock類的源代碼。在這里我們可以看到在ReentrantLock類中還包含了一個(gè)繼承自AbstractQueuedSynchronizer類的內(nèi)部類,而且有一個(gè)該內(nèi)部類Sync類型的字段sync。實(shí)際上ReentrantLock類就是通過這個(gè)內(nèi)部類對(duì)象來實(shí)現(xiàn)線程同步的。

如果打開CountDownLatch的源代碼,我們會(huì)發(fā)現(xiàn)這個(gè)類里也同樣有一個(gè)繼承自AbstractQueuedSynchronizer類的子類Sync,并且也有一個(gè)Sync類型的字段sync。在java.util.concurrent包下的大多數(shù)同步工具類的底層都是通過在內(nèi)部定義一個(gè)AbstractQueuedSynchronizer類的子類來實(shí)現(xiàn)的,包括我們在本文中沒提到的許多其他常用類也是如此,比如:讀寫鎖ReentrantReadWriteLock、信號(hào)量Semaphore等。

AQS是什么?

那么這個(gè)AbstractQueuedSynchronizer類也就是我們所說的AQS,到底是何方神圣呢?這個(gè)類首先像我們上面提到的,是大多數(shù)多線程同步工具類的基礎(chǔ)。它內(nèi)部包含了一個(gè)對(duì)同步器的等待隊(duì)列,其中包含了所有在等待獲取同步器的線程,在這個(gè)等待隊(duì)列中的線程將會(huì)在同步器釋放時(shí)被喚醒。比如一個(gè)線程在獲取互斥鎖失敗時(shí)就會(huì)被放入到等待隊(duì)列中等待被喚醒,這也就是AQS中的Q——“Queued”的由來。

而類名中的***個(gè)單詞Abstract是因?yàn)锳QS是一個(gè)抽象類,它的使用方法就是實(shí)現(xiàn)繼承它的子類,然后使用這個(gè)子類類型的對(duì)象。在這個(gè)子類中我們會(huì)通過重寫下列的五個(gè)方法中的一部分或者全部來指定這個(gè)同步器的行為策略:

  1.  boolean tryAcquire(int arg),獨(dú)占式獲取同步器,獨(dú)占式指同一時(shí)間只能有一個(gè)線程獲取到同步器;
  2.  boolean tryRelease(int arg),獨(dú)占式釋放同步器;
  3.  boolean isHeldExclusively(),同步器是否被當(dāng)前線程獨(dú)占式地持有;
  4.  int tryAcquireShared(int arg),共享式獲取同步器,共享式指的是同一時(shí)間可能有多個(gè)線程同時(shí)獲取到同步器,但是可能會(huì)有數(shù)量的限制;
  5.  boolean tryReleaseShared(int arg),共享式釋放同步器。

這五個(gè)方法之所以能指定同步器的行為,則是因?yàn)锳QS中的其他方法就是通過對(duì)這五個(gè)方法的調(diào)用來實(shí)現(xiàn)的。比如在下面的acquire方法中就調(diào)用了tryAcquire來獲取同步器,并且在被調(diào)用的acquireQueued方法內(nèi)部也是通過tryAcquire方法來循環(huán)嘗試獲取同步器的。 

  1. public final void acquire(int arg) {  
  2.     // 1. 調(diào)用tryAcquire方法嘗試獲取鎖  
  3.     // 2. 如果獲取失敗(tryAcquire返回false),則調(diào)用addWaiter方法將當(dāng)前線程保存到等待隊(duì)列中  
  4.     // 3. 之后調(diào)用acquireQueued方法來循環(huán)執(zhí)行“獲取同步器 -> 獲取失敗休眠 -> 被喚醒重新獲取”過程  
  5.     //      直到成功獲取到同步器返回false;或者被中斷返回true  
  6.     if (!tryAcquire(arg) &&  
  7.         acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  
  8.         // 如果acquireQueued方法返回true說明線程被中斷了  
  9.         //   所以調(diào)用selfInterrupt方法中斷當(dāng)前線程  
  10.         selfInterrupt();  

下面,我們就來看看在ReentrantLock和CountDownLatch兩個(gè)類中定義的AQS子類到底是如何重寫這五個(gè)方法的。

CountDownLatch的實(shí)現(xiàn)

CountDownLatch是一種典型的閉鎖,比如我需要使用四個(gè)線程完成四種不同的計(jì)算,然后把四個(gè)線程的計(jì)算結(jié)果相加后返回,這種情況下主線程就需要等待四個(gè)完成不同任務(wù)的工作線程完成之后才能繼續(xù)執(zhí)行。那么我們就可以創(chuàng)建一個(gè)初始的count值為4的CountDownLatch,然后在每個(gè)工作線程完成任務(wù)時(shí)都對(duì)這個(gè)CountDownLatch執(zhí)行一個(gè)countDown操作,這樣CountDownLatch中的count值就會(huì)減1。當(dāng)count值減到0時(shí),主線程就會(huì)從阻塞中恢復(fù),然后將四個(gè)任務(wù)的結(jié)果相加后返回。

下面是CountDownLath的幾個(gè)常用方法:

  1.  void await(),等待操作,如果count值目前已經(jīng)是0了,那么就直接返回;否則就進(jìn)入阻塞狀態(tài),等待count值變?yōu)?;
  2.  void countDown(),減少計(jì)數(shù)操作,會(huì)讓count減1。

調(diào)用多次countDown()方法讓count值變?yōu)?之后,被await()方法阻塞的線程就可以繼續(xù)執(zhí)行了。了解了CountDownLatch的基本用法之后我們就來看看這個(gè)閉鎖到底是怎么實(shí)現(xiàn)的,首先,我們來看一下CountDownLatch中AQS的子類,內(nèi)部類Sync的定義。

CountDownLatch的內(nèi)部Sync類

下面的代碼是CountDownLatch中AQS的子類Sync的定義,Sync是CountDownLatch類中的一個(gè)內(nèi)部類。在這個(gè)類中重寫了AQS的tryAcquireShared和tryReleaseShared兩個(gè)方法,這兩個(gè)都是共享模式需要重寫的方法,因?yàn)镃ountDownLatch在count值為0時(shí)可以被任意多個(gè)線程同時(shí)獲取成功,所以應(yīng)該實(shí)現(xiàn)共享模式的方法。

在CountDownLatch的Sync中使用了AQS的state值用來存放count值,在初始化時(shí)會(huì)把state值初始化為n。然后在調(diào)用tryReleaseShared時(shí)會(huì)將count值減1,但是因?yàn)檫@個(gè)方法可能會(huì)被多個(gè)線程同時(shí)調(diào)用,所以要用CAS操作保證更新操作的原子性,就像我們用AtomicInteger一樣。在CAS失敗時(shí)我們需要通過重試來保證把state減1,如果CAS成功時(shí),即使有許多線程同時(shí)執(zhí)行這個(gè)操作***的結(jié)果也一定是正確的。在這里,tryReleaseShared方法的返回值表示這個(gè)釋放操作是否可以讓等待中的線程成功獲取同步器,所以只有在count為0時(shí)才能返回true。

tryAcquireShared方法就比較簡單了,直接返回state是否等于0即可,因?yàn)橹挥性贑ountDownLatch中的count值為0時(shí)所有希望獲取同步器的線程才能獲取成功并繼續(xù)執(zhí)行。如果count不為0,那么線程就需要進(jìn)入阻塞狀態(tài),等到count值變?yōu)?才能繼續(xù)執(zhí)行。 

  1. private static final class Sync extends AbstractQueuedSynchronizer {  
  2.     private static final long serialVersionUID = 4982264981922014374L 
  3.     // 構(gòu)造器,初始化count值  
  4.     // 在這個(gè)子類中把count值保存到了AQS的state中  
  5.     Sync(int count) {  
  6.         setState(count);  
  7.     }  
  8.     // 獲取當(dāng)前的count值  
  9.     int getCount() {  
  10.         return getState();  
  11.     }  
  12.     // 獲取操作在state為0時(shí)會(huì)成功,否則失敗  
  13.     // tryAcquireShared失敗時(shí),線程會(huì)進(jìn)入阻塞狀態(tài)等待獲取成功  
  14.     protected int tryAcquireShared(int acquires) {  
  15.         return (getState() == 0) ? 1 : -1;  
  16.     }  
  17.     // 對(duì)閉鎖執(zhí)行釋放操作減小計(jì)數(shù)值  
  18.     protected boolean tryReleaseShared(int releases) {  
  19.         // 減小coun值,在count值歸零時(shí)喚醒等待的線程  
  20.         for (;;) {  
  21.             int c = getState();  
  22.             // 如果計(jì)數(shù)已經(jīng)歸零,則直接釋放失敗  
  23.             if (c == 0)  
  24.                 return false;  
  25.             // 將計(jì)數(shù)值減1  
  26.             int nextc = c-1;  
  27.             // 為了線程安全,以CAS循環(huán)嘗試更新  
  28.             if (compareAndSetState(c, nextc))  
  29.                 return nextc == 0;  
  30.         }  
  31.     }  

CounDownLatch對(duì)Sync類對(duì)象的使用

看了CountDownLatch中的Sync內(nèi)部類定義之后,我們再來看看CountDownLatch是如何使用這個(gè)內(nèi)部類的。

在CountDownLatch的構(gòu)造器中,初始化CountDownLatch對(duì)象時(shí)會(huì)同時(shí)在其內(nèi)部初始化保存一個(gè)Sync類型的對(duì)象到sync字段用于之后的同步操作。并且傳入Sync類構(gòu)造器的count一定會(huì)大于等于0。 

  1. public CountDownLatch(int count) {  
  2.     if (count < 0) throw new IllegalArgumentException("count < 0");  
  3.     this.sync = new Sync(count);  

有了Sync類型的對(duì)象之后,我們在await()方法里就可以直接調(diào)用sync的acquireSharedInterruptibly方法來獲取同步器并陷入阻塞,等待count值變?yōu)?了。在AQS的acquireSharedInterruptibly方法中會(huì)在調(diào)用我們重寫的tryAcquireShared方法獲取失敗時(shí)進(jìn)入阻塞狀態(tài),直到CountDownLatch的count值變?yōu)?時(shí)才能成功獲取到同步器。 

  1. public void await() throws InterruptedException {  
  2.     // 調(diào)用sync對(duì)象的獲取方法來進(jìn)入鎖等待  
  3.     sync.acquireSharedInterruptibly(1);  

而在CountDownLatch的另一個(gè)減少count值的重要方法countDown()中,我們同樣是通過調(diào)用sync上的方法來實(shí)現(xiàn)具體的同步功能。在這里,AQS的releaseShared(1)方法中同樣會(huì)調(diào)用我們在Sync類中重寫的tryReleaseShared方法來執(zhí)行釋放操作,并在tryReleaseShared方法返回true時(shí)去喚醒等待隊(duì)列中的阻塞等待線程,讓它們在count值為0時(shí)能夠繼續(xù)執(zhí)行。 

  1. public void countDown() {  
  2.     sync.releaseShared(1);  

從上文中可以看出,CoundDownLatch中的各種功能都是通過內(nèi)部類Sync來實(shí)現(xiàn)的,而這個(gè)Sync類就是一個(gè)繼承自AQS的子類。通過在內(nèi)部類Sync中重寫了AQS的tryAcquireShared和tryReleaseShared兩個(gè)方法,我們就指定了AQS的行為策略,使其能夠符合我們對(duì)CountDownLatch功能的期望。這就是AQS的使用方法,下面我們來看一個(gè)大家可能會(huì)更熟悉的例子,來進(jìn)一步了解AQS在獨(dú)占模式下的用法。

ReentrantLock的實(shí)現(xiàn)

可重入鎖ReentrantLock可以說是我們的老朋友了,從最早的synchronized關(guān)鍵字開始,我們就開始使用類似的功能了。可重入鎖的特點(diǎn)主要有兩點(diǎn):

  •  同一時(shí)間只能有一個(gè)線程持有
    •   如果我想保護(hù)一段代碼同一時(shí)間只能被一個(gè)線程所訪問,比如對(duì)一個(gè)隊(duì)列的插入操作。那么如果有一個(gè)線程已經(jīng)獲取了鎖之后在修改隊(duì)列了,那么其他也想要修改隊(duì)列的線程就會(huì)陷入阻塞,等待之前的這個(gè)線程執(zhí)行完成。
  •  同一線程可以對(duì)一個(gè)鎖重復(fù)獲取成功多次
    •   而如果一個(gè)線程對(duì)同一個(gè)隊(duì)列執(zhí)行了兩個(gè)插入操作,那么第二次獲取鎖時(shí)仍然會(huì)成功,而不會(huì)被***次成功獲取到的鎖所阻塞。

ReentrantLock類的常用操作主要有三種:

  •  獲取鎖,一個(gè)線程一旦獲取鎖成功后就會(huì)阻塞其他線程獲取同一個(gè)鎖的操作,所以一旦獲取失敗,那么當(dāng)前線程就會(huì)被阻塞
    •   最簡單的獲取鎖方法就是調(diào)用public void lock()方法
  •  釋放鎖,獲取鎖之后就要在使用完之后釋放它,否則別的線程都將會(huì)因無法獲取鎖而被阻塞,所以我們一般會(huì)在finally中進(jìn)行鎖的釋放操作
    •   可以通過調(diào)用ReentrantLock對(duì)象的unlock方法來釋放鎖
  •  獲取條件變量,條件變量是和互斥鎖搭配使用的一種非常有用的數(shù)據(jù)結(jié)構(gòu),有興趣的讀者可以通過《從0到1實(shí)現(xiàn)自己的阻塞隊(duì)列(上)》這篇文章來了解條件變量具體的使用方法
    •   我們可以通過Condition newCondition()方法來獲取條件變量對(duì)象,然后調(diào)用條件變量對(duì)象上的await()、signal()、signalAll()方法來進(jìn)行使用

ReentrantLock的內(nèi)部Sync類

在ReentrantLock類中存在兩種AQS的子類,一個(gè)實(shí)現(xiàn)了非公平鎖,一個(gè)實(shí)現(xiàn)了公平鎖。所謂的“公平”指的就是獲取互斥鎖成功返回的時(shí)間會(huì)和獲取鎖操作發(fā)起的時(shí)間順序一致,例如有線程A已經(jīng)持有了互斥鎖,當(dāng)線程B、C、D按字母順序獲取鎖并進(jìn)入等待,線程A釋放鎖后一定是線程B被喚醒,線程B釋放鎖后一定是C先被喚醒。也就是說鎖被釋放后對(duì)等待線程的喚醒順序和獲取鎖操作的順序一致。而且如果在這個(gè)過程中,有其他線程發(fā)起了獲取鎖操作,因?yàn)榈却?duì)列中已經(jīng)有線程在等待了,那么這個(gè)線程一定要排到等待隊(duì)列***去,而不能直接搶占剛剛被釋放還未被剛剛被喚醒的線程鎖持有的鎖。

下面我們同樣先看一下ReentrantLock類中定義的AQS子類Sync的具體源代碼。下面是上一段說到的非公平Sync類和公平Sync類兩個(gè)類的共同父類Sync的帶注釋源代碼,里面包含了大部分核心功能的實(shí)現(xiàn)。雖然下面包含了該類完整的源代碼,但是我們現(xiàn)在只需要關(guān)心三個(gè)核心操作,也是我們在獨(dú)占模式下需要重寫的三個(gè)AQS方法:tryAcquire、tryRelease和isHeldExclusively。建議在看完文章之后再回來回顧該類中其他的方法實(shí)現(xiàn),直接跳過其他的方法當(dāng)然也是完全沒有問題的。 

  1. abstract static class Sync extends AbstractQueuedSynchronizer {  
  2.     private static final long serialVersionUID = -5179523762034025860L;  
  3.     /**  
  4.      * 實(shí)現(xiàn)Lock接口的lock方法,子類化的主要原因是為了非公平版本的快速實(shí)現(xiàn)    
  5.       */  
  6.     abstract void lock();  
  7.     /**  
  8.      * 執(zhí)行非公平的tryLock。tryAcquire方法在子類中被實(shí)現(xiàn),但是兩者都需要非公平版本的trylock方法實(shí)現(xiàn)。  
  9.      */  
  10.     final boolean nonfairTryAcquire(int acquires) {  
  11.         final Thread current = Thread.currentThread();  
  12.         int c = getState();  
  13.         // 如果鎖還未被持有  
  14.         if (c == 0) {  
  15.             // 通過CAS嘗試獲取鎖  
  16.             if (compareAndSetState(0, acquires)) {  
  17.                 // 如果鎖獲取成功則將鎖持有者改為當(dāng)前線程,并返回true  
  18.                 setExclusiveOwnerThread(current);  
  19.                 return true;  
  20.             }  
  21.         }  
  22.         // 鎖已經(jīng)被持有,則判斷鎖的持有者是否是當(dāng)前線程  
  23.         else if (current == getExclusiveOwnerThread()) {  
  24.             // 可重入鎖,如果鎖的持有者是當(dāng)前線程,那就在state上加上新的獲取數(shù)  
  25.             int nextc = c + acquires;  
  26.             // 判斷新的state值有沒有溢出  
  27.             if (nextc < 0) // overflow  
  28.                 throw new Error("Maximum lock count exceeded");  
  29.             // 將新的state更新為新的值,因?yàn)榭梢赃M(jìn)入這段代碼的只有一個(gè)線程  
  30.             // 所以不需要線程安全措施  
  31.             setState(nextc);  
  32.             return true;  
  33.         }      
  34.          return false;  
  35.     }  
  36.     // 重寫了AQS的獨(dú)占式釋放鎖方法  
  37.     protected final boolean tryRelease(int releases) {  
  38.         // 計(jì)算剩余的鎖持有量  
  39.         // 因?yàn)橹挥挟?dāng)前線程持有該鎖的情況下才能執(zhí)行這個(gè)方法,所以不需要做多線程保護(hù)  
  40.         int c = getState() - releases;  
  41.         // 如果當(dāng)前線程未持有鎖,則直接拋出錯(cuò)誤  
  42.         if (Thread.currentThread() != getExclusiveOwnerThread())  
  43.             throw new IllegalMonitorStateException();  
  44.         boolean free = false 
  45.         // 如果鎖持有數(shù)已經(jīng)減少到0,則釋放該鎖,并清空鎖持有者  
  46.         if (c == 0) {  
  47.             free = true 
  48.             setExclusiveOwnerThread(null);  
  49.         }  
  50.         // 更新state值,只有state值被設(shè)置為0才是真正地釋放了鎖  
  51.         // 所以setState和setExclusiveOwnerThread之間不需要額外的同步措施  
  52.         setState(c);  
  53.         return free;  
  54.     }   
  55.     // 當(dāng)前線程是否持有該鎖  
  56.     protected final boolean isHeldExclusively() {  
  57.         return getExclusiveOwnerThread() == Thread.currentThread();  
  58.     }  
  59.     // 創(chuàng)建對(duì)應(yīng)的條件變量  
  60.     final ConditionObject newCondition() {  
  61.         return new ConditionObject();  
  62.     }  
  63.     // 從外層傳遞進(jìn)來的方法  
  64.     // 獲取當(dāng)前的鎖持有者  
  65.     final Thread getOwner() {  
  66.         return getState() == 0 ? null : getExclusiveOwnerThread();  
  67.     }  
  68.     // 獲取鎖的持有計(jì)數(shù)  
  69.     // 如果當(dāng)前線程持有了該鎖則返回state值,否則返回0  
  70.     final int getHoldCount() {  
  71.         return isHeldExclusively() ? getState() : 0;  
  72.     }  
  73.     // 判斷鎖是否已經(jīng)被持有  
  74.     final boolean isLocked() {  
  75.         return getState() != 0;  
  76.     }  

實(shí)際的tryAcquire方法將在公平Sync類與非公平Sync類兩個(gè)子類中實(shí)現(xiàn),但是這兩個(gè)子類都需要調(diào)用父類Sync中的非公平版本的tryAcquire——nonfairTryAcquire方法。在這個(gè)方法中,我們主要做兩件事:

  •  當(dāng)前鎖還未被人持有。在ReentrantLock中使用AQS的state來保存鎖的狀態(tài),state等于0時(shí)代表鎖沒有被任何線程持有,如果state大于0,那么就代表持有者對(duì)該鎖的重復(fù)獲取次數(shù)
    •   如果當(dāng)前鎖還未被線程持有,那么就會(huì)通過compareAndSetState來原子性地修改state值,修改成功則需要設(shè)置當(dāng)前線程為鎖的持有線程并返回true代表獲取成功;否則就返回
  •  鎖已被當(dāng)前線程持有
    •   在鎖已被當(dāng)前線程持有的情況下,就需要將state值加1代表持有者線程對(duì)鎖的重復(fù)獲取次數(shù)。

而對(duì)于獨(dú)占式釋放同步器的tryRelease方法,則在父類Sync中直接實(shí)現(xiàn)了,兩個(gè)公平/非公平子類調(diào)用的都是同一段代碼。首先,只有鎖的持有者才能釋放鎖,所以如果當(dāng)前線程不是所有者線程在釋放操作中就會(huì)拋出異常。如果釋放操作會(huì)將持有計(jì)數(shù)清零,那么當(dāng)前線程就不再是該鎖的持有者了,鎖會(huì)被完全釋放,而鎖的所有者會(huì)被設(shè)置為null。***,Sync會(huì)將減掉入?yún)⒅械尼尫艛?shù)之后的新持有計(jì)數(shù)更新到AQS的state中,并返回鎖是否已經(jīng)被完全釋放了。

isHeldExclusively方法比較簡單,它只是檢查鎖的持有者是否是當(dāng)前線程。

非公平Sync類的實(shí)現(xiàn)

Sync的兩個(gè)公平/非公平子類的實(shí)現(xiàn)比較簡單,下面是非公平版本子類的源代碼。在非公平版本的實(shí)現(xiàn)中,調(diào)用lock方法首先會(huì)嘗試通過CAS修改AQS的state值來直接搶占鎖,如果搶占成功就直接將持有者設(shè)置為當(dāng)前線程;如果搶占失敗就調(diào)用acquire方法走正常流程來獲取鎖。而在acquire方法中就會(huì)調(diào)用子類中的tryAcquire方法并進(jìn)一步調(diào)用到上文提到的父類中的nonfairTryAcquire方法來完成鎖獲取操作。 

  1. static final class NonfairSync extends Sync {  
  2.     private static final long serialVersionUID = 7316153563782823691L 
  3.     /**  
  4.      * 執(zhí)行鎖操作。嘗試直接搶占,如果失敗的話就回到正常的獲取流程進(jìn)行  
  5.      */  
  6.     final void lock() {  
  7.         // 嘗試直接搶占  
  8.         if (compareAndSetState(0, 1))  
  9.             // 搶占成功設(shè)置鎖所有者  
  10.             setExclusiveOwnerThread(Thread.currentThread());  
  11.         else  
  12.             // 搶占失敗走正常獲取流程  
  13.             acquire(1);  
  14.     }  
  15.     // 實(shí)現(xiàn)AQS方法,使用nonfairTryAcquire實(shí)現(xiàn)  
  16.     protected final boolean tryAcquire(int acquires) {  
  17.         return nonfairTryAcquire(acquires);  
  18.     }  

公平Sync類的實(shí)現(xiàn)

而在公平版本的Sync子類FairSync中,為了保證成功獲取到鎖的順序一定要和發(fā)起獲取鎖操作的順序一致,所以自然不能在lock方法中進(jìn)行CAS方式的搶占,只能老老實(shí)實(shí)調(diào)用acquire方法走正式流程。而acquire方法最終就會(huì)調(diào)用子類中定義的tryAcquire來真正獲取鎖。

在tryAcquire方法中,代碼主要處理了兩種情況:

  •  當(dāng)前鎖還沒有被線程鎖持有
    •   只有在確保等待隊(duì)列為空的情況下才能嘗試用CAS方式直接搶占鎖,而在等待隊(duì)列不為空的情況下,***返回了false,之后acquire方法中的代碼會(huì)將當(dāng)前線程放入到等待隊(duì)列中阻塞等待鎖的釋放。這就保證了在獲取鎖時(shí)已經(jīng)有線程等待的情況下,任何線程都要進(jìn)入等待隊(duì)列去等待獲取鎖,而不能直接對(duì)鎖進(jìn)行獲取。
  •  當(dāng)前線程已經(jīng)持有了該鎖
    •   如果當(dāng)前線程已經(jīng)是該鎖的持有者了,那么就會(huì)在state值上加上本次的獲取數(shù)量來更新鎖的重復(fù)獲取次數(shù),并返回true代表獲取鎖成功。 
  1. static final class FairSync extends Sync {  
  2.     private static final long serialVersionUID = -3000897897090466540L;  
  3.     // 直接使用acquire進(jìn)行獲取鎖操作  
  4.     final void lock() {  
  5.         acquire(1);  
  6.     }  
  7.     /**  
  8.      * 公平版本的tryAcquire方法。不要授予訪問權(quán)限,除非是遞歸調(diào)用或者沒有等待線程或者這是***個(gè)調(diào)用  
  9.      */  
  10.     protected final boolean tryAcquire(int acquires) {  
  11.         final Thread current = Thread.currentThread();  
  12.         int c = getState();  
  13.         // 如果鎖沒有被持有  
  14.         if (c == 0) {  
  15.             // 為了實(shí)現(xiàn)公平特性,所以只有在等待隊(duì)列為空的情況下才能直接搶占  
  16.             // 否則只能進(jìn)入隊(duì)列等待  
  17.             if (!hasQueuedPredecessors() &&  
  18.                 compareAndSetState(0, acquires)) {  
  19.                 setExclusiveOwnerThread(current);  
  20.                 return true;  
  21.             }  
  22.         }  
  23.         // 如果鎖已被持有,且當(dāng)前線程就是持有線程  
  24.         else if (current == getExclusiveOwnerThread()) {  
  25.             // 計(jì)算新的state值  
  26.             int nextc = c + acquires;  
  27.             // 如果鎖計(jì)數(shù)溢出,則拋出異常  
  28.             if (nextc < 0 
  29.                 throw new Error("Maximum lock count exceeded");  
  30.             // 設(shè)置state狀態(tài)值  
  31.             setState(nextc);  
  32.             return true;  
  33.         }  
  34.         return false;  
  35.     }  

ReentrantLock對(duì)Sync類對(duì)象的使用

***,我們來看看ReentrantLock類中的lock()、unlock()、newCondition方法對(duì)Sync類對(duì)象的使用方式。

首先是在構(gòu)造器中,根據(jù)入?yún)⒅付ǖ墓?非公平模式創(chuàng)建不同的內(nèi)部Sync類對(duì)象,如果是公平模式就是用FairSync類,如果是非公平模式就是用NonfairSync類。 

  1. public ReentrantLock(boolean fair) {  
  2.     sync = fair ? new FairSync() : new NonfairSync();  

然后在互斥鎖的鎖定方法lock()中,ReentrantLock直接使用Sync類中的lock方法來實(shí)現(xiàn)了鎖的獲取功能。 

  1. public void lock() {  
  2.     // 調(diào)用sync對(duì)象的lock方法實(shí)現(xiàn)  
  3.     sync.lock();  

在unlock()方法中也是一樣的情況,ReentrantLock直接依賴Sync類對(duì)象來實(shí)現(xiàn)這個(gè)功能。 

  1. public void unlock() {  
  2.     // 調(diào)用了sync對(duì)象的release方法實(shí)現(xiàn)  
  3.     sync.release(1);  

***一個(gè)創(chuàng)建條件變量的方法則直接依賴于AQS中定義的方法,我們在ReentranctLock的Sync類中并不需要做任務(wù)額外的工作,AQS就能為我們做好所有的事情。 

  1. public Condition newCondition() {  
  2.     // 調(diào)用了sync對(duì)象繼承自AQS的`newCondition`方法實(shí)現(xiàn)  
  3.     return sync.newCondition();  

通過ReentrantLock的例子我們能夠更明顯地感受到,這些基于AQS實(shí)現(xiàn)同步功能的類中并不需要做太多額外的工作,大多數(shù)操作都是通過直接調(diào)用Sync類對(duì)象上的方法來實(shí)現(xiàn)的。只要定義好了繼承自AQS的子類Sync,并通過Sync類重寫幾個(gè)AQS的關(guān)鍵方法來指定AQS的行為策略,就可以實(shí)現(xiàn)風(fēng)格迥異的各種同步工具類了。

總結(jié)

在這篇文章中,我們從AQS的基本概念說起,簡單介紹了AQS的具體用法,然后通過CountDownLatch和ReentrantLock兩個(gè)常用的多線程同步工具類的源碼來具體了解了AQS的使用方式。我們不僅可以完全弄明白這兩個(gè)線程同步類的實(shí)現(xiàn)原理與細(xì)節(jié),而且最重要的是找到了AQS這個(gè)幕后大BOSS。通過AQS,我們不僅可以更容易地閱讀并理解其他同步工具類的使用與實(shí)現(xiàn),而且甚至可以動(dòng)手開發(fā)出我們自己的自定義同步工具類。

到了這里,這一系列多線程編程相關(guān)的技術(shù)文章就接近尾聲了。后續(xù)我還會(huì)發(fā)布一篇囊括這個(gè)系列所有內(nèi)容的總結(jié)性文章,里面會(huì)對(duì)多線程編程相關(guān)的知識(shí)脈絡(luò)做一次全面的梳理,然后將每個(gè)知識(shí)點(diǎn)鏈接到具體闡釋這個(gè)主題的文章中去。讓讀者可以在宏觀和微觀兩個(gè)層面理解多線程編程的原理與技巧,幫助大家建立完整的Java多線程理論與實(shí)踐知識(shí)體系。有興趣的讀者可以關(guān)注一下后續(xù)的文章,感謝大家的支持。 

責(zé)任編輯:龐桂玉 來源: segmentfault
相關(guān)推薦

2011-11-23 10:09:19

Java線程機(jī)制

2009-08-12 13:37:01

Java synchr

2010-03-15 16:31:34

Java多線程

2012-07-09 09:25:13

ibmdw

2017-12-15 10:20:56

MySQLInnoDB同步機(jī)制

2016-09-20 15:21:35

LinuxInnoDBMysql

2012-07-27 10:02:39

C#

2024-07-05 08:32:36

2025-03-31 00:01:12

2024-06-28 08:45:58

2021-10-08 20:30:12

ZooKeeper選舉機(jī)制

2019-08-22 14:30:21

技術(shù)Redis設(shè)計(jì)

2019-11-22 18:52:31

進(jìn)程同步機(jī)制編程語言

2024-07-08 12:51:05

2024-07-25 11:53:53

2021-03-23 07:56:54

JS基礎(chǔ)同步異步編程EventLoop底層

2011-06-22 13:57:54

Java多線程

2011-06-22 13:47:16

Java多線程

2022-12-26 09:27:48

Java底層monitor

2024-08-28 08:00:00

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)