1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問(wèn)題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
      ReentrantLock源碼解析是什么

      ReentrantLock源碼解析是什么,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。

      我們提供的服務(wù)有:成都網(wǎng)站建設(shè)、成都做網(wǎng)站、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、吉安ssl等。為上千家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的吉安網(wǎng)站制作公司

      什么是可重鎖ReentrantLock?

      那么什么是重入鎖呢?

      重入鎖(遞歸鎖)可以理解為:同一個(gè)線程函數(shù)獲得鎖之后,內(nèi)層遞歸函數(shù)依然能夠獲取到該鎖對(duì)象的代碼,也即,在同一個(gè)線程的外層方法訪問(wèn)的時(shí)候,獲取到了鎖,在進(jìn)入內(nèi)層方法后能夠自動(dòng)獲取到鎖。線程可以進(jìn)入任何一個(gè)它已經(jīng)擁有的鎖所同步著的代碼塊。額,說(shuō)的啥意思?每個(gè)中文都認(rèn)識(shí),但是組合在一起,就不知道啥意思了。

      我們來(lái)舉個(gè)生活中的例子:

      在現(xiàn)實(shí)生活中,我們一般只需要帶有自己大門(mén)的鑰匙(當(dāng)然,如果是合租的朋友還需要帶著自己房間的鑰匙)。當(dāng)我們開(kāi)了大門(mén)的鑰匙,進(jìn)入房間后,我們?cè)谌N房或者是去衛(wèi)生間的時(shí)候,不用在拿鑰匙開(kāi)廚房或者衛(wèi)生間的門(mén)了吧。為啥呢?因?yàn)槲覀円呀?jīng)已經(jīng)有大門(mén)的鎖的鑰匙并且已經(jīng)進(jìn)入到了房間了。廚房和衛(wèi)生間已經(jīng)在大門(mén)鎖管理的范圍內(nèi)了。這種場(chǎng)景站在并發(fā)鎖的角度來(lái)看的話:一同一個(gè)線程函數(shù)獲得鎖之后(你拿著鑰匙打開(kāi)了大門(mén)之后),內(nèi)層遞歸函數(shù)依然能夠獲取到該鎖對(duì)象的代碼(進(jìn)入房間后,房間內(nèi)的廚房衛(wèi)生間可以隨便出入)。這樣是不是就好理解了?

      底層實(shí)現(xiàn)原理主要是利用通過(guò)繼承AQS來(lái)實(shí)現(xiàn)的,也是利用通過(guò)對(duì)volatile state的CAS操作+CLH隊(duì)列來(lái)實(shí)現(xiàn);

      CAS:Compare and Swap 比較并交換。CAS的思想很簡(jiǎn)單:3個(gè)參數(shù),一個(gè)當(dāng)前內(nèi)存值V、預(yù)期值A(chǔ),即將更新的值B,當(dāng)前僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相等的時(shí)候,將內(nèi)存值V修改為B,否則什么都不做。該操作是一個(gè)原子操作被廣泛的用于java的底層實(shí)現(xiàn)中,在java中,CAS主要是有sun.misc.Unsafe這個(gè)類通過(guò)JNI調(diào)用CPU底層指令實(shí)現(xiàn);更多底層的思想?yún)⒖祭歉绲奈恼耤as的底層原理:https://www.jianshu.com/p/fb6e91b013cc

      CLH隊(duì)列:也叫同步隊(duì)列,是帶頭結(jié)點(diǎn)的雙向非循環(huán)列表,是AQS的主要實(shí)現(xiàn)原理(結(jié)構(gòu)如下圖所示)

      ReentrantLock源碼解析是什么

      最常用的方式:

      int a = 12;
              //注意:通常情況下,這個(gè)會(huì)設(shè)置成一個(gè)類變量,比如說(shuō)Segement中的段鎖與copyOnWriteArrayList中的全局鎖
              final ReentrantLock lock = new ReentrantLock();
              
              lock.lock();//獲取鎖
              try {
                  a++;//業(yè)務(wù)邏輯
              } catch (Exception e) {
              }finally{
                  lock.unlock();//釋放鎖
              }

      1、對(duì)于ReentrantLock需要掌握以下幾點(diǎn)

      • ReentrantLock的創(chuàng)建(公平鎖/非公平鎖)

      • 上鎖:lock()

      • 解鎖:unlock()

      首先說(shuō)一下類結(jié)構(gòu):

      • ReentrantLock-->Lock

      • NonfairSync/FairSync-->Sync-->AbstractQueuedSynchronizer-->AbstractOwnableSynchronizer

      • NonfairSync/FairSync-->Sync是ReentrantLock的三個(gè)內(nèi)部類

      • Node是AbstractQueuedSynchronizer的內(nèi)部類

      注意:上邊這四條線,對(duì)應(yīng)關(guān)系:"子類"-->"父類"

      2、ReentrantLock的創(chuàng)建

      • 支持公平鎖(先進(jìn)來(lái)的線程先執(zhí)行)

      • 支持非公平鎖(后進(jìn)來(lái)的線程也可能先執(zhí)行)

      非公平鎖與非公平鎖的創(chuàng)建

      • 非公平鎖:ReentrantLock()或ReentrantLock(false)

        final ReentrantLock lock = new ReentrantLock();

      • 公平鎖:ReentrantLock(true)

        final ReentrantLock lock = new ReentrantLock(true)

      默認(rèn)情況下使用非公平鎖。

      源代碼如下:

      ReentrantLock:

      /** 同步器:內(nèi)部類Sync的一個(gè)引用 */
          private final Sync sync;
      
          /**
           * 創(chuàng)建一個(gè)非公平鎖
           */
          public ReentrantLock() {
              sync = new NonfairSync();
          }
      
          /**
           * 創(chuàng)建一個(gè)鎖
           * @param fair true-->公平鎖  false-->非公平鎖
           */
          public ReentrantLock(boolean fair) {
              sync = (fair)? new FairSync() : new NonfairSync();
          }

      上述源代碼中出現(xiàn)了三個(gè)內(nèi)部類Sync/NonfairSync/FairSync,這里只列出類的定義,至于這三個(gè)類中的具體的方法會(huì)在后續(xù)的第一次引用的時(shí)候介紹。

      Sync/NonfairSync/FairSync類定義:

      /*
           * 該鎖同步控制的一個(gè)基類.下邊有兩個(gè)子類:非公平機(jī)制和公平機(jī)制.使用了AbstractQueuedSynchronizer類的
           */
          static abstract class Sync extends AbstractQueuedSynchronizer
      
          /**
           * 非公平鎖同步器
           */
          final static class NonfairSync extends Sync
      
          /**
           * 公平鎖同步器
           */
          final static class FairSync extends Sync

      3、非公平鎖的lock()

      具體使用方法:

      lock.lock();

      下面先介紹一下這個(gè)總體步驟的簡(jiǎn)化版,然后會(huì)給出詳細(xì)的源代碼,并在源代碼的lock()方法部分給出詳細(xì)版的步驟。

      簡(jiǎn)化版的步驟:(非公平鎖的核心)

      基于CAS嘗試將state(鎖數(shù)量)從0設(shè)置為1

      A、如果設(shè)置成功,設(shè)置當(dāng)前線程為獨(dú)占鎖的線程;

      B、如果設(shè)置失敗,還會(huì)再獲取一次鎖數(shù)量,

      B1、如果鎖數(shù)量為0,再基于CAS嘗試將state(鎖數(shù)量)從0設(shè)置為1一次,如果設(shè)置成功,設(shè)置當(dāng)前線程為獨(dú)占鎖的線程;

      B2、如果鎖數(shù)量不為0或者上邊的嘗試又失敗了,查看當(dāng)前線程是不是已經(jīng)是獨(dú)占鎖的線程了,如果是,則將當(dāng)前的鎖數(shù)量+1;如果不是,則將該線程封裝在一個(gè)Node內(nèi),并加入到等待隊(duì)列中去。等待被其前一個(gè)線程節(jié)點(diǎn)喚醒。

      源代碼:(再介紹源代碼之前,心里有一個(gè)獲取鎖的步驟的總的一個(gè)印象,就是上邊這個(gè)"簡(jiǎn)化版的步驟")

      3.1、ReentrantLock:lock()

      /**
           *獲取一個(gè)鎖
           *三種情況:
           *1、如果當(dāng)下這個(gè)鎖沒(méi)有被任何線程(包括當(dāng)前線程)持有,則立即獲取鎖,鎖數(shù)量==1,之后再執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
           *2、如果當(dāng)前線程正在持有這個(gè)鎖,那么鎖數(shù)量+1,之后再執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
           *3、如果當(dāng)下鎖被另一個(gè)線程所持有,則當(dāng)前線程處于休眠狀態(tài),直到獲得鎖之后,當(dāng)前線程被喚醒,鎖數(shù)量==1,再執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
           */
          public void lock() {
              sync.lock();//調(diào)用NonfairSync(非公平鎖)或FairSync(公平鎖)的lock()方法
          }

      3.2、NonfairSync:lock()

      /**
               * 1)首先基于CAS將state(鎖數(shù)量)從0設(shè)置為1,如果設(shè)置成功,設(shè)置當(dāng)前線程為獨(dú)占鎖的線程;-->請(qǐng)求成功-->第一次插隊(duì)
               * 2)如果設(shè)置失敗(即當(dāng)前的鎖數(shù)量可能已經(jīng)為1了,即在嘗試的過(guò)程中,已經(jīng)被其他線程先一步占有了鎖),這個(gè)時(shí)候當(dāng)前線程執(zhí)行acquire(1)方法
               * 2.1)acquire(1)方法首先調(diào)用下邊的tryAcquire(1)方法,在該方法中,首先獲取鎖數(shù)量狀態(tài),
               * 2.1.1)如果為0(證明該獨(dú)占鎖已被釋放,當(dāng)下沒(méi)有線程在使用),這個(gè)時(shí)候我們繼續(xù)使用CAS將state(鎖數(shù)量)從0設(shè)置為1,如果設(shè)置成功,當(dāng)前線程獨(dú)占鎖;-->請(qǐng)求成功-->第二次插隊(duì);當(dāng)然,如果設(shè)置不成功,直接返回false
               * 2.2.2)如果不為0,就去判斷當(dāng)前的線程是不是就是當(dāng)下獨(dú)占鎖的線程,如果是,就將當(dāng)前的鎖數(shù)量狀態(tài)值+1(這也就是可重入鎖的名稱的來(lái)源)-->請(qǐng)求成功
               * 
               * 下邊的流程一句話:請(qǐng)求失敗后,將當(dāng)前線程鏈入隊(duì)尾并掛起,之后等待被喚醒。
               * 
               * 2.2.3)如果最后在tryAcquire(1)方法中上述的執(zhí)行都沒(méi)成功,即請(qǐng)求沒(méi)有成功,則返回false,繼續(xù)執(zhí)行acquireQueued(addWaiter(Node.EXCLUSIVE), arg)方法
               * 2.2)在上述方法中,首先會(huì)使用addWaiter(Node.EXCLUSIVE)將當(dāng)前線程封裝進(jìn)Node節(jié)點(diǎn)node,然后將該節(jié)點(diǎn)加入等待隊(duì)列(先快速入隊(duì),如果快速入隊(duì)不成功,其使用正常入隊(duì)方法無(wú)限循環(huán)一直到Node節(jié)點(diǎn)入隊(duì)為止)
               * 2.2.1)快速入隊(duì):如果同步等待隊(duì)列存在尾節(jié)點(diǎn),將使用CAS嘗試將尾節(jié)點(diǎn)設(shè)置為node,并將之前的尾節(jié)點(diǎn)插入到node之前
               * 2.2.2)正常入隊(duì):如果同步等待隊(duì)列不存在尾節(jié)點(diǎn)或者上述CAS嘗試不成功的話,就執(zhí)行正常入隊(duì)(該方法是一個(gè)無(wú)限循環(huán)的過(guò)程,即直到入隊(duì)為止)-->第一次阻塞
               * 2.2.2.1)如果尾節(jié)點(diǎn)為空(初始化同步等待隊(duì)列),創(chuàng)建一個(gè)dummy節(jié)點(diǎn),并將該節(jié)點(diǎn)通過(guò)CAS嘗試設(shè)置到頭節(jié)點(diǎn)上去,設(shè)置成功的話,將尾節(jié)點(diǎn)也指向該dummy節(jié)點(diǎn)(即頭節(jié)點(diǎn)和尾節(jié)點(diǎn)都指向該dummy節(jié)點(diǎn))
               * 2.2.2.1)如果尾節(jié)點(diǎn)不為空,執(zhí)行與快速入隊(duì)相同的邏輯,即使用CAS嘗試將尾節(jié)點(diǎn)設(shè)置為node,并將之前的尾節(jié)點(diǎn)插入到node之前
               * 最后,如果順利入隊(duì)的話,就返回入隊(duì)的節(jié)點(diǎn)node,如果不順利的話,無(wú)限循環(huán)去執(zhí)行2.2)下邊的流程,直到入隊(duì)為止
               * 2.3)node節(jié)點(diǎn)入隊(duì)之后,就去執(zhí)行acquireQueued(final Node node, int arg)(這又是一個(gè)無(wú)限循環(huán)的過(guò)程,這里需要注意的是,無(wú)限循環(huán)等于阻塞,多個(gè)線程可以同時(shí)無(wú)限循環(huán)--每個(gè)線程都可以執(zhí)行自己的循環(huán),這樣才能使在后邊排隊(duì)的節(jié)點(diǎn)不斷前進(jìn))
               * 2.3.1)獲取node的前驅(qū)節(jié)點(diǎn)p,如果p是頭節(jié)點(diǎn),就繼續(xù)使用tryAcquire(1)方法去嘗試請(qǐng)求成功,-->第三次插隊(duì)(當(dāng)然,這次插隊(duì)不一定不會(huì)使其獲得執(zhí)行權(quán),請(qǐng)看下邊一條),
               * 2.3.1.1)如果第一次請(qǐng)求就成功,不用中斷自己的線程,如果是之后的循環(huán)中將線程掛起之后又請(qǐng)求成功了,使用selfInterrupt()中斷自己
               * (注意p==head&&tryAcquire(1)成功是唯一跳出循環(huán)的方法,在這之前會(huì)一直阻塞在這里,直到其他線程在執(zhí)行的過(guò)程中,不斷的將p的前邊的節(jié)點(diǎn)減少,直到p成為了head且node請(qǐng)求成功了--即node被喚醒了,才退出循環(huán))
               * 2.3.1.2)如果p不是頭節(jié)點(diǎn),或者tryAcquire(1)請(qǐng)求不成功,就去執(zhí)行shouldParkAfterFailedAcquire(Node pred, Node node)來(lái)檢測(cè)當(dāng)前節(jié)點(diǎn)是不是可以安全的被掛起,
               * 2.3.1.2.1)如果node的前驅(qū)節(jié)點(diǎn)pred的等待狀態(tài)是SIGNAL(即可以喚醒下一個(gè)節(jié)點(diǎn)的線程),則node節(jié)點(diǎn)的線程可以安全掛起,執(zhí)行2.3.1.3)
               * 2.3.1.2.2)如果node的前驅(qū)節(jié)點(diǎn)pred的等待狀態(tài)是CANCELLED,則pred的線程被取消了,我們會(huì)將pred之前的連續(xù)幾個(gè)被取消的前驅(qū)節(jié)點(diǎn)從隊(duì)列中剔除,返回false(即不能掛起),之后繼續(xù)執(zhí)行2.3)中上述的代碼
               * 2.3.1.2.3)如果node的前驅(qū)節(jié)點(diǎn)pred的等待狀態(tài)是除了上述兩種的其他狀態(tài),則使用CAS嘗試將前驅(qū)節(jié)點(diǎn)的等待狀態(tài)設(shè)為SIGNAL,并返回false(因?yàn)镃AS可能會(huì)失敗,這里不管失敗與否,都返回false,下一次執(zhí)行該方法的之后,pred的等待狀態(tài)就是SIGNAL了),之后繼續(xù)執(zhí)行2.3)中上述的代碼
               * 2.3.1.3)如果可以安全掛起,就執(zhí)行parkAndCheckInterrupt()掛起當(dāng)前線程,之后,繼續(xù)執(zhí)行2.3)中之前的代碼
               * 最后,直到該節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)p之前的所有節(jié)點(diǎn)都執(zhí)行完畢為止,我們的p成為了頭節(jié)點(diǎn),并且tryAcquire(1)請(qǐng)求成功,跳出循環(huán),去執(zhí)行。
               * (在p變?yōu)轭^節(jié)點(diǎn)之前的整個(gè)過(guò)程中,我們發(fā)現(xiàn)這個(gè)過(guò)程是不會(huì)被中斷的)
               * 2.3.2)當(dāng)然在2.3.1)中產(chǎn)生了異常,我們就會(huì)執(zhí)行cancelAcquire(Node node)取消node的獲取鎖的意圖。
               */
              final void lock() {
                  if (compareAndSetState(0, 1))//如果CAS嘗試成功
                      setExclusiveOwnerThread(Thread.currentThread());//設(shè)置當(dāng)前線程為獨(dú)占鎖的線程
                  else
                      acquire(1);
              }

      注意:在這個(gè)方法中,我列出了一個(gè)線程獲取鎖的詳細(xì)的過(guò)程,自己看注釋。

      下面列出NonfairSync:lock()中調(diào)用的幾個(gè)方法與相關(guān)屬性。

      3.2.1、AbstractQueuedSynchronizer:鎖數(shù)量state屬性+相關(guān)方法:

      /**
           * 鎖數(shù)量
           */
          private volatile int state;
      
          /**
           * 獲取鎖數(shù)量
           */
          protected final int getState() {
              return state;
          }
      
          protected final void setState(int newState) {
              state = newState;
          }

      注意:state是volatile型的

      3.2.2、AbstractOwnableSynchronizer:屬性+setExclusiveOwnerThread(Thread t)

      /**
           * 當(dāng)前擁有獨(dú)占鎖的線程
           */
          private transient Thread exclusiveOwnerThread;
      
          /**
           * 設(shè)置獨(dú)占鎖的線程為線程t
           */
          protected final void setExclusiveOwnerThread(Thread t) {
              exclusiveOwnerThread = t;
          }

      3.2.3、AbstractQueuedSynchronizer:屬性+acquire(int arg)

      /**
           * 獲取鎖的方法
           * @param arg
           */
          public final void acquire(int arg) {
              if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                  selfInterrupt();//中斷自己
          }

      在介紹上邊這個(gè)方法之前,先要說(shuō)一下AbstractQueuedSynchronizer的一個(gè)內(nèi)部類Node的整體構(gòu)造,源代碼如下:

      /*
           * 同步等待隊(duì)列(雙向鏈表)中的節(jié)點(diǎn)
           */
          static final class Node {
              /** 線程被取消了 */
              static final int CANCELLED = 1;
              /** 
               * 如果前驅(qū)節(jié)點(diǎn)的等待狀態(tài)是SIGNAL,表示當(dāng)前節(jié)點(diǎn)將來(lái)可以被喚醒,那么當(dāng)前節(jié)點(diǎn)就可以安全的掛起了 
               * 否則,當(dāng)前節(jié)點(diǎn)不能掛起 
               */
              static final int SIGNAL = -1;
              /**線程正在等待條件*/
              static final int CONDITION = -2;
              /**
               * waitStatus value to indicate the next acquireShared should
               * unconditionally propagate
               */
              static final int PROPAGATE = -3;
              /** Marker to indicate a node is waiting in shared mode */
              static final Node SHARED = new Node();
              /** 一個(gè)標(biāo)記:用于表明該節(jié)點(diǎn)正在獨(dú)占鎖模式下進(jìn)行等待 */
              static final Node EXCLUSIVE = null;
              //值就是前四個(gè)int(CANCELLED/SIGNAL/CONDITION/PROPAGATE),再加一個(gè)0
      
              volatile int waitStatus;
              /**前驅(qū)節(jié)點(diǎn)*/
              volatile Node prev;
      
              /**后繼節(jié)點(diǎn)*/
              volatile Node next;
      
              /**節(jié)點(diǎn)中的線程*/
              volatile Thread thread;
      
              /**
               * Link to next node waiting on condition, or the special value SHARED.
               * Because condition queues are accessed only when holding in exclusive
               * mode, we just need a simple linked queue to hold nodes while they are
               * waiting on conditions. They are then transferred to the queue to
               * re-acquire. And because conditions can only be exclusive, we save a
               * field by using special value to indicate shared mode.
               */
              Node nextWaiter;
      
              /**
               * Returns true if node is waiting in shared mode
               */
              final boolean isShared() {
                  return nextWaiter == SHARED;
              }
      
              /**
               * 返回該節(jié)點(diǎn)前一個(gè)節(jié)點(diǎn)
               */
              final Node predecessor() throws NullPointerException {
                  Node p = prev;
                  if (p == null)
                      throw new NullPointerException();
                  else
                      return p;
              }
      
              Node() { // Used to establish initial head or SHARED marker
              }
      
              Node(Thread thread, Node mode) { // 用于addWaiter中
                  this.nextWaiter = mode;
                  this.thread = thread;
              }
      
              Node(Thread thread, int waitStatus) { // Used by Condition
                  this.waitStatus = waitStatus;
                  this.thread = thread;
              }
          }

      注意:這里我給出了Node類的完整版,其中部分屬性與方法是在共享鎖的模式下使用的,而我們這里的ReentrantLock是一個(gè)獨(dú)占鎖,只需關(guān)注其中的與獨(dú)占鎖相關(guān)的部分就好(具體有注釋)

      3.3、AbstractQueuedSynchronizer:acquire(int arg)方法中使用到的兩個(gè)方法

      3.3.1、NonfairSync:tryAcquire(int acquires)

      /**
               * 試著請(qǐng)求成功
               */
              protected final boolean tryAcquire(int acquires) {
                  return nonfairTryAcquire(acquires);
              }

      Syn:

      /**
               * 非公平鎖中被tryAcquire調(diào)用
               */
              final boolean nonfairTryAcquire(int acquires) {
                  final Thread current = Thread.currentThread();//獲取當(dāng)前線程
                  int c = getState();//獲取鎖數(shù)量
                  if (c == 0) {//如果鎖數(shù)量為0,證明該獨(dú)占鎖已被釋放,當(dāng)下沒(méi)有線程在使用
                      if (compareAndSetState(0, acquires)) {//繼續(xù)通過(guò)CAS將state由0變?yōu)?,注意這里傳入的acquires為1
                          setExclusiveOwnerThread(current);//將當(dāng)前線程設(shè)置為獨(dú)占鎖的線程
                          return true;
                      }
                  }
                  else if (current == getExclusiveOwnerThread()) {//查看當(dāng)前線程是不是就是獨(dú)占鎖的線程
                      int nextc = c + acquires;//如果是,鎖狀態(tài)的數(shù)量為當(dāng)前的鎖數(shù)量+1
                      if (nextc < 0) // overflow
                          throw new Error("Maximum lock count exceeded");
                      setState(nextc);//設(shè)置當(dāng)前的鎖數(shù)量
                      return true;
                  }
                  return false;
              }

      注意:這個(gè)方法就完成了"簡(jiǎn)化版的步驟"中的"A/B/B1"三步,如果上述的請(qǐng)求不能成功,就要執(zhí)行下邊的代碼了,

      下邊的代碼,用一句話介紹:請(qǐng)求失敗后,將當(dāng)前線程鏈入隊(duì)尾并掛起,之后等待被喚醒。在你看下邊的代碼的時(shí)候心里默記著這句話。

      3.3.2、AbstractQueuedSynchronizer:addWaiter(Node mode)

      /**
           * 將Node節(jié)點(diǎn)加入等待隊(duì)列
           * 1)快速入隊(duì),入隊(duì)成功的話,返回node
           * 2)入隊(duì)失敗的話,使用正常入隊(duì)
           * 注意:快速入隊(duì)與正常入隊(duì)相比,可以發(fā)現(xiàn),正常入隊(duì)僅僅比快速入隊(duì)多而一個(gè)判斷隊(duì)列是否為空且為空之后的過(guò)程
           * @return 返回當(dāng)前要插入的這個(gè)節(jié)點(diǎn),注意不是前一個(gè)節(jié)點(diǎn)
           */
          private Node addWaiter(Node mode) {
              Node node = new Node(Thread.currentThread(), mode);//創(chuàng)建節(jié)點(diǎn)
              /*
               * 快速入隊(duì)
               */
              Node pred = tail;//將尾節(jié)點(diǎn)賦給pred
              if (pred != null) {//尾節(jié)點(diǎn)不為空
                  node.prev = pred;//將尾節(jié)點(diǎn)作為創(chuàng)造出來(lái)的節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn),即將node鏈接到為節(jié)點(diǎn)后
                  /**
                   * 基于CAS將node設(shè)置為尾節(jié)點(diǎn),如果設(shè)置失敗,說(shuō)明在當(dāng)前線程獲取尾節(jié)點(diǎn)到現(xiàn)在這段過(guò)程中已經(jīng)有其他線程將尾節(jié)點(diǎn)給替換過(guò)了
                   * 注意:假設(shè)有鏈表node1-->node2-->pred(當(dāng)然是雙鏈表,這里畫(huà)成雙鏈表才合適),
                   * 通過(guò)CAS將pred替換成了node節(jié)點(diǎn),即當(dāng)下的鏈表為node1-->node2-->node,
                   * 然后根據(jù)上邊的"node.prev = pred"與下邊的"pred.next = node"將pred插入到雙鏈表中去,組成最終的鏈表如下:
                   * node1-->node2-->pred-->node
                   * 這樣的話,實(shí)際上我們發(fā)現(xiàn)沒(méi)有指定node2.next=pred與pred.prev=node2,這是為什么呢?
                   * 因?yàn)樵谥斑@兩句就早就執(zhí)行好了,即node2.next和pred.prev這連個(gè)屬性之前就設(shè)置好了
                   */
                  if (compareAndSetTail(pred, node)) {
                      pred.next = node;//將node放在尾節(jié)點(diǎn)上
                      return node;
                  }
              }
              enq(node);//正常入隊(duì)
              return node;
          }

      AbstractQueuedSynchronizer:enq(final Node node)

      /**
           * 正常入隊(duì)
           * @param node
           * @return 之前的尾節(jié)點(diǎn)
           */
          private Node enq(final Node node) {
              for (;;) {//無(wú)限循環(huán),一定要阻塞到入隊(duì)成功為止
                  Node t = tail;//獲取尾節(jié)點(diǎn)
                  if (t == null) { //如果尾節(jié)點(diǎn)為null,說(shuō)明當(dāng)前等待隊(duì)列為空
                      /*Node h = new Node(); // Dummy header
                      h.next = node;
                      node.prev = h;
                      if (compareAndSetHead(h)) {//根據(jù)代碼實(shí)際上是:compareAndSetHead(null,h)
                          tail = node;
                          return h;
                      }*/
                      /*
                       * 注意:上邊注釋掉的這一段代碼是jdk1.6.45中的,在后來(lái)的版本中,這一段改成了如下這段
                       * 基于CAS將新節(jié)點(diǎn)(一個(gè)dummy節(jié)點(diǎn))設(shè)置到頭上head去,如果發(fā)現(xiàn)內(nèi)存中的當(dāng)前值不是null,則說(shuō)明,在這個(gè)過(guò)程中,已經(jīng)有其他線程設(shè)置過(guò)了。
                       * 當(dāng)成功的將這個(gè)dummy節(jié)點(diǎn)設(shè)置到head節(jié)點(diǎn)上去時(shí),我們又將這個(gè)head節(jié)點(diǎn)設(shè)置給了tail節(jié)點(diǎn),即head與tail都是當(dāng)前這個(gè)dummy節(jié)點(diǎn),
                       * 之后有新節(jié)點(diǎn)入隊(duì)的話,就插入到該dummy之后
                       */
                      if (compareAndSetHead(new Node()))
                          tail = head;
                  } else {//這一塊兒的邏輯與快速入隊(duì)完全相同
                      node.prev = t;
                      if (compareAndSetTail(t, node)) {//嘗試將node節(jié)點(diǎn)設(shè)為尾節(jié)點(diǎn)
                          t.next = node;//將node節(jié)點(diǎn)設(shè)為尾節(jié)點(diǎn)
                          return t;
                      }
                  }
              }
          }

      注意:這里就是一個(gè)完整的入隊(duì)方法,具體邏輯看注釋和ReentrantLock:lock()的注釋部分的相關(guān)部分。

      3.3.3、AbstractQueuedSynchronizer:acquireQueued(final Node node, int arg)

      final boolean acquireQueued(final Node node, int arg) {
              try {
                  boolean interrupted = false;
                  /*
                   * 無(wú)限循環(huán)(一直阻塞),直到node的前驅(qū)節(jié)點(diǎn)p之前的所有節(jié)點(diǎn)都執(zhí)行完畢,p成為了head且node請(qǐng)求成功了
                   */
                  for (;;) {
                      final Node p = node.predecessor();//獲取插入節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)p
                      /*
                       * 注意:
                       * 1、這個(gè)是跳出循環(huán)的唯一條件,除非拋異常
                       * 2、如果p == head && tryAcquire(arg)第一次循環(huán)就成功了,interrupted為false,不需要中斷自己
                       *         如果p == head && tryAcquire(arg)第一次以后的循環(huán)中如果執(zhí)行了掛起操作后才成功了,interrupted為true,就要中斷自己了
                       */
                      if (p == head && tryAcquire(arg)) {
                          setHead(node);//當(dāng)前節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn)
                          p.next = null; 
                          return interrupted;//跳出循環(huán)
                      }
                      if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                          interrupted = true;//被中斷了
                  }
              } catch (RuntimeException ex) {
                  cancelAcquire(node);
                  throw ex;
              }
          }

      AbstractQueuedSynchronizer:shouldParkAfterFailedAcquire(Node pred, Node node)

      /**
           * 檢測(cè)當(dāng)前節(jié)點(diǎn)是否可以被安全的掛起(阻塞)
           * @param pred    當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
           * @param node    當(dāng)前節(jié)點(diǎn)
           */
          private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
              int ws = pred.waitStatus;//獲取前驅(qū)節(jié)點(diǎn)(即當(dāng)前線程的前一個(gè)節(jié)點(diǎn))的等待狀態(tài)
              if (ws == Node.SIGNAL)//如果前驅(qū)節(jié)點(diǎn)的等待狀態(tài)是SIGNAL,表示當(dāng)前節(jié)點(diǎn)將來(lái)可以被喚醒,那么當(dāng)前節(jié)點(diǎn)就可以安全的掛起了
                  return true;
              /*
               * 1)當(dāng)ws>0(即CANCELLED==1),前驅(qū)節(jié)點(diǎn)的線程被取消了,我們會(huì)將該節(jié)點(diǎn)之前的連續(xù)幾個(gè)被取消的前驅(qū)節(jié)點(diǎn)從隊(duì)列中剔除,返回false(即不能掛起)
               * 2)如果ws<=0&&!=SIGNAL,將當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的等待狀態(tài)設(shè)為SIGNAL
               */
              if (ws > 0) {
                  do {
                      /*
                       * node.prev = pred = pred.prev;
                       * 上邊這句代碼相當(dāng)于下邊這兩句
                       * pred = pred.prev;
                       * node.prev = pred;
                       */
                      node.prev = pred = pred.prev;
                  } while (pred.waitStatus > 0);
                  pred.next = node;
              } else {
                  /*
                   * 嘗試將當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的等待狀態(tài)設(shè)為SIGNAL
                   * 1/這為什么用CAS,現(xiàn)在已經(jīng)入隊(duì)成功了,前驅(qū)節(jié)點(diǎn)就是pred,除了node外應(yīng)該沒(méi)有別的線程在操作這個(gè)節(jié)點(diǎn)了,那為什么還要用CAS?而不直接賦值呢?
                   * (解釋:因?yàn)閜red可以自己將自己的狀態(tài)改為cancel,也就是pred的狀態(tài)可能同時(shí)會(huì)有兩條線程(pred和node)去操作)
                   * 2/既然前驅(qū)節(jié)點(diǎn)已經(jīng)設(shè)為SIGNAL了,為什么最后還要返回false
                   * (因?yàn)镃AS可能會(huì)失敗,這里不管失敗與否,都返回false,下一次執(zhí)行該方法的之后,pred的等待狀態(tài)就是SIGNAL了)
                   */
                  compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
              }
              return false;
          }

      AbstractQueuedSynchronizer:

      private final boolean parkAndCheckInterrupt() {
              LockSupport.park(this);//掛起當(dāng)前的線程
              return Thread.interrupted();//如果當(dāng)前線程已經(jīng)被中斷了,返回true
          }

      以上就是一個(gè)線程獲取非公平鎖的整個(gè)過(guò)程(lock())。

      4、公平鎖的lock()

      具體用法與非公平鎖一樣

      如果掌握了非公平鎖的流程,那么掌握公平鎖的流程會(huì)非常簡(jiǎn)單,只有兩點(diǎn)不同(最后會(huì)講)。

      簡(jiǎn)化版的步驟:(公平鎖的核心)

      獲取一次鎖數(shù)量,

      B1、如果鎖數(shù)量為0,如果當(dāng)前線程是等待隊(duì)列中的頭節(jié)點(diǎn),基于CAS嘗試將state(鎖數(shù)量)從0設(shè)置為1一次,如果設(shè)置成功,設(shè)置當(dāng)前線程為獨(dú)占鎖的線程;

      B2、如果鎖數(shù)量不為0或者當(dāng)前線程不是等待隊(duì)列中的頭節(jié)點(diǎn)或者上邊的嘗試又失敗了,查看當(dāng)前線程是不是已經(jīng)是獨(dú)占鎖的線程了,如果是,則將當(dāng)前的鎖數(shù)量+1;如果不是,則將該線程封裝在一個(gè)Node內(nèi),并加入到等待隊(duì)列中去。等待被其前一個(gè)線程節(jié)點(diǎn)喚醒。

      源代碼:

      4.1、ReentrantLock:lock()

      /**
           *獲取一個(gè)鎖
           *三種情況:
           *1、如果當(dāng)下這個(gè)鎖沒(méi)有被任何線程(包括當(dāng)前線程)持有,則立即獲取鎖,鎖數(shù)量==1,之后被喚醒再執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
           *2、如果當(dāng)前線程正在持有這個(gè)鎖,那么鎖數(shù)量+1,之后被喚醒再執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
           *3、如果當(dāng)下鎖被另一個(gè)線程所持有,則當(dāng)前線程處于休眠狀態(tài),直到獲得鎖之后,當(dāng)前線程被喚醒,鎖數(shù)量==1,再執(zhí)行相應(yīng)的業(yè)務(wù)邏輯
           */
          public void lock() {
              sync.lock();//調(diào)用FairSync的lock()方法
          }

      4.2、FairSync:lock()

      final void lock() {
                  acquire(1);
              }

      4.3、AbstractQueuedSynchronizer:acquire(int arg)就是非公平鎖使用的那個(gè)方法

      4.3.1、FairSync:tryAcquire(int acquires)

      /**
               * 獲取公平鎖的方法
               * 1)獲取鎖數(shù)量c
               * 1.1)如果c==0,如果當(dāng)前線程是等待隊(duì)列中的頭節(jié)點(diǎn),使用CAS將state(鎖數(shù)量)從0設(shè)置為1,如果設(shè)置成功,當(dāng)前線程獨(dú)占鎖-->請(qǐng)求成功
               * 1.2)如果c!=0,判斷當(dāng)前的線程是不是就是當(dāng)下獨(dú)占鎖的線程,如果是,就將當(dāng)前的鎖數(shù)量狀態(tài)值+1(這也就是可重入鎖的名稱的來(lái)源)-->請(qǐng)求成功
               * 最后,請(qǐng)求失敗后,將當(dāng)前線程鏈入隊(duì)尾并掛起,之后等待被喚醒。
               */
              protected final boolean tryAcquire(int acquires) {
                  final Thread current = Thread.currentThread();
                  int c = getState();
                  if (c == 0) {
                      if (isFirst(current) && compareAndSetState(0, acquires)) {
                          setExclusiveOwnerThread(current);
                          return true;
                      }
                  }
                  else if (current == getExclusiveOwnerThread()) {
                      int nextc = c + acquires;
                      if (nextc < 0)
                          throw new Error("Maximum lock count exceeded");
                      setState(nextc);
                      return true;
                  }
                  return false;
              }

      最后,如果請(qǐng)求失敗后,將當(dāng)前線程鏈入隊(duì)尾并掛起,之后等待被喚醒,下邊的代碼與非公平鎖一樣。

      總結(jié):公平鎖與非公平鎖對(duì)比

      • FairSync:lock()少了插隊(duì)部分(即少了CAS嘗試將state從0設(shè)為1,進(jìn)而獲得鎖的過(guò)程)

      • FairSync:tryAcquire(int acquires)多了需要判斷當(dāng)前線程是否在等待隊(duì)列首部的邏輯(實(shí)際上就是少了再次插隊(duì)的過(guò)程,但是CAS獲取還是有的)。

      最后說(shuō)一句,

      • ReentrantLock是基于AbstractQueuedSynchronizer實(shí)現(xiàn)的,AbstractQueuedSynchronizer可以實(shí)現(xiàn)獨(dú)占鎖也可以實(shí)現(xiàn)共享鎖,ReentrantLock只是使用了其中的獨(dú)占鎖模式

      • 這一塊兒代碼比較多,邏輯比較復(fù)雜,最好在閱讀的過(guò)程中,可以拿一根筆畫(huà)畫(huà)入隊(duì)等與數(shù)據(jù)結(jié)構(gòu)相關(guān)的圖

      • 一定要記住"簡(jiǎn)化版的步驟",這是整個(gè)非公平鎖與公平鎖的核心

      看完上述內(nèi)容,你們掌握 ReentrantLock源碼解析是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!


      網(wǎng)頁(yè)名稱:ReentrantLock源碼解析是什么
      網(wǎng)頁(yè)網(wǎng)址:http://www.ef60e0e.cn/article/geggoc.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        绥滨县| 时尚| 郎溪县| 长葛市| 义马市| 广州市| 从江县| 广饶县| 横峰县| 赤水市| 曲周县| 怀集县| 克山县| 行唐县| 伊吾县| 小金县| 葫芦岛市| 永平县| 年辖:市辖区| 镇赉县| 宁夏| 茶陵县| 焦作市| 涟源市| 镇赉县| 东光县| 登封市| 西青区| 香格里拉县| 平遥县| 万载县| 英德市| 阳新县| 长春市| 锦屏县| 藁城市| 新田县| 盘锦市| 怀宁县| 东乡县| 上栗县|