<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          終于會了,java鎖的底層原理

          共 12268字,需瀏覽 25分鐘

           ·

          2021-05-19 02:37

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”

          優(yōu)質(zhì)文章,第一時間送達(dá)

          知識整理

          1. Synchronized 內(nèi)置鎖,JVM級別

            1. 代碼優(yōu)化:同步代碼塊、減少鎖粒度、讀鎖并發(fā)

            2. JDK自帶 偏置鎖、輕量級鎖(CAS操作)、自適應(yīng)自旋、鎖粗化、鎖消除

            3. 使用

            4. 底層 鎖升級過程、CAS操作的缺點(diǎn)【替換線程和copy mw】

            5. 優(yōu)化

          2. Volatile

            1. 概念:非阻塞可見性、禁止指令重排序*

            2. 與syn區(qū)別: 無法實(shí)現(xiàn)原子操作、使用場景--單線程、不依賴當(dāng)前值


          3. Reentrantlock 顯示鎖:基于AQS實(shí)現(xiàn),API級別

            1. 數(shù)據(jù)結(jié)構(gòu):state、waitstate【signal-1、傳播-3】、

            2. 獨(dú)占、共享 tryAcquireShared

            3. AQS原理:

            4. 非公平鎖

            5. 特性鎖 可重入、輪詢、定時、可中斷

            6. 優(yōu)點(diǎn)、使用場景

            7. 與Syn區(qū)別、Syn優(yōu)點(diǎn)

          4. 死鎖

            1. 檢測算法:資源分配表、遍歷鎖關(guān)系圖

            2. 撤銷進(jìn)程、設(shè)置線程隨機(jī)優(yōu)先級

            3. 概念:多個線程因競爭資源而互相等待的僵局;4個必要條件:資源互斥、不可剝奪、保持與請求、循環(huán)等待

            4. 死鎖避免:鎖順序、鎖時限、死鎖檢測與恢復(fù)

            5. 死鎖檢測與恢復(fù):分配資源時不加條件;檢測時機(jī):進(jìn)程等待、定時、利用率下降

          5. 鎖模式

            1. 讀鎖、寫鎖

            2. 樂觀鎖:用戶解決---數(shù)據(jù)版本id、時間戳;CAS;適合寫操作少的場景;MVCC實(shí)現(xiàn)

            3. 悲觀鎖:數(shù)據(jù)庫行鎖、頁鎖...

          synchronized的4種應(yīng)用方式 jvm內(nèi)部實(shí)現(xiàn) 稱為:內(nèi)置鎖

          synchronized關(guān)鍵字最主要有以下3種應(yīng)用方式,都是作用在對象上

          1. 修飾類,作用范圍:synchronized括號內(nèi), 作用對象:類的所有對象;synchronized(Service.class){ }

          2. 修改靜態(tài)方法,作用范圍:整個靜態(tài)方法, 作用對象:類的所有對象;

          3. 修飾方法,被修飾的同步方法,作用范圍:整個方法, 作用對象:調(diào)用這個方法的對象;

            1. 缺點(diǎn):A線程執(zhí)行一個長時間任務(wù),B線程必須等待

          4. 修飾代碼塊,被修飾的代碼塊同步語句塊,作用范圍:大括號內(nèi)的代碼, 作用對象:調(diào)用這個代碼塊的對象;

            1. 優(yōu)點(diǎn):減少鎖范圍,耗時的代碼放外面,可以異步調(diào)用

           

          notify 方法實(shí)現(xiàn)只喚醒一個線程,由操作

          lock.notify()方法最終通過ObjectMonitor的void notify(TRAPS)實(shí)現(xiàn):

          1、如果當(dāng)前_WaitSet【線程等待的集合】為空,即沒有正在等待的線程,則直接返回;

          2、通過ObjectMonitor::DequeueWaiter 出隊方法,獲取_WaitSet列表中的第一個ObjectWaiter節(jié)點(diǎn),實(shí)現(xiàn)也很簡單【選擇哪個線程取決于操作系統(tǒng)對多線程管理的實(shí)現(xiàn)】

          3、根據(jù)不同的策略,將取出來的ObjectWaiter節(jié)點(diǎn),加入到Contention List,或自旋操作,CAS改變第一個節(jié)點(diǎn)的的指針為新增節(jié)點(diǎn) 

          notifyAll方法實(shí)現(xiàn)

          lock.notifyAll()方法最終通過ObjectMonitor的void notifyAll(TRAPS)實(shí)現(xiàn):

          通過for循環(huán)取出_WaitSet的ObjectWaiter節(jié)點(diǎn),并根據(jù)不同策略,加入到_EntryList或則進(jìn)行自旋操作。

          從JVM的方法實(shí)現(xiàn)中,可以發(fā)現(xiàn):notify和notifyAll并不會釋放所占有的ObjectMonitor對象,其實(shí)真正釋放ObjectMonitor對象的時間點(diǎn)是在執(zhí)行monitorexit指令,

          一旦釋放ObjectMonitor對象了,Entryset中ObjectWaiter節(jié)點(diǎn)所保存的線程,就可以開始競爭ObjectMonitor對象進(jìn)行加鎖操作了,和ready線程競爭?

           

          2.鎖的的實(shí)現(xiàn):內(nèi)存機(jī)制 copy到工作內(nèi)存->   修改->   刷新主存   的過程,才會釋放它得到的鎖,達(dá)到線程安全。

          1. 鎖住(lock)

          2. 主->從 read load 將需要的數(shù)據(jù)從主內(nèi)存拷貝到自己的工作內(nèi)存(read and load)

          3. 修改 use assign 根據(jù)程序流程讀取或者修改相應(yīng)變量值(use and assign)

          4. 從->主 store write將自己工作內(nèi)存中修改了值的變量拷貝回主內(nèi)存(store and write)

          5. 釋放對象鎖(unlock)

          線程安全:

          1. 當(dāng)多個線程訪問某個類,其始終能表現(xiàn)出正確的行為

          2. 采用了加鎖機(jī)制,當(dāng)一個線程訪問該類的某個數(shù)據(jù)時,進(jìn)行保護(hù),限制其他線程訪問,直到鎖釋放 

          Java中的鎖優(yōu)化 代碼方式、JDK自帶方式

          1.代碼 鎖優(yōu)化

          1. 減少鎖持有時間 

            1. 使用同步代碼塊,而非同步方法;

          2. 減小鎖粒度

            1. JDK1.6中 ConcurrentHashMap采取對segment加鎖而不是整個map加鎖,提高并發(fā)性;

          3. 鎖分離  讀鎖之間不互斥;讀寫分離

            1. 根據(jù)同步操作的性質(zhì),把鎖劃分為的讀鎖和寫鎖,讀鎖之間不互斥,提高了并發(fā)性

          2.JDK1.6 鎖優(yōu)化 synchronized底層

          1.引入偏向鎖、輕量級鎖

          1. 鎖主要存在四中狀態(tài),依次是:無鎖狀態(tài)01、偏向鎖狀態(tài)01、輕量級鎖狀態(tài)00、重量級鎖狀態(tài)10,

          2. 會隨著競爭的激烈而逐漸升級,鎖可以升級不可降級,提高 獲得鎖和釋放鎖 效率

          3. “輕量級鎖”和“偏向鎖”作用:減少 獲得鎖和釋放鎖 的性能消耗

          優(yōu)點(diǎn)

          缺點(diǎn)

          適用場景

          偏向鎖

          記錄線程iD,若該線程,則不加鎖;鎖狀態(tài)01

          如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗。

          適用于只有一個線程訪問同步塊場景。

          輕量級鎖

          Mark Word復(fù)制到鎖記錄,CAS更新指針及標(biāo)志位00

          自旋方式競爭,競爭的線程不會阻塞,提高了程序的響應(yīng)速度

          如果始終得不到鎖競爭的線程使用,自旋會消耗CPU。

          追求響應(yīng)時間。

          同步塊執(zhí)行速度非???。

          重量級鎖

          CAS失敗時,升級。鎖狀態(tài):10

          線程阻塞,響應(yīng)時間緩慢。

          追求吞吐量。

          同步塊執(zhí)行速度較長。

          偏向鎖獲取過程

          1. 訪問Mark Word中偏向鎖的標(biāo)識是否設(shè)置成1,鎖標(biāo)志位是否為01,確認(rèn)為可偏向狀態(tài)。

          2. 如果為可偏向狀態(tài),則判斷偏向線程ID是否指向當(dāng)前線程,如果是,進(jìn)入步驟5,否則進(jìn)入步驟3。

          3. 如果線程ID并未指向當(dāng)前線程,則通過CAS操作競爭鎖。如果競爭成功,則將Mark Word中線程ID設(shè)置為當(dāng)前線程ID,然后執(zhí)行5;

            1. 如果競爭失敗,執(zhí)行4,偏向鎖升級為輕量級鎖。

          4. 如果CAS獲取偏向鎖失敗,則表示有競爭。當(dāng)?shù)竭_(dá)全局安全點(diǎn)(safepoint)時獲得偏向鎖的線程被掛起,偏向鎖升級為輕量級鎖,然后被阻塞在安全點(diǎn)的線程繼續(xù)往下執(zhí)行同步代碼。(撤銷偏向鎖的時候會導(dǎo)致stop the word)

          5. 執(zhí)行同步代碼

          輕量級鎖

          1. 虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲鎖對象目前的Mark Word的拷貝【因為棧幀為線程私有,對象大家都有】

          2. 拷貝對象頭中的Mark Word復(fù)制到鎖記錄(Lock Record)中;

          3. 拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對象的Mark Word中的,更新為指向Lock Record的指針,并將Lock record里的owner指針指向object mark word。如果更新成功,則執(zhí)行步驟4,否則執(zhí)行步驟5。

          4. 如果這個更新動作成功了,那么這個線程就擁有了該對象的鎖,并且對象Mark Word的鎖標(biāo)志位設(shè)置為“00”,即表示此對象處于輕量級鎖定狀態(tài),這時候線程堆棧與對象頭的狀態(tài)如圖所示。

           

          2.鎖粗化 

          1. 如果一系列的連續(xù)操作都對同一個對象反復(fù)加鎖和解鎖,如循環(huán)體內(nèi),很耗性能

          2. 加鎖同步的范圍擴(kuò)展到整個操作序列的外部:第一個append到最后一個append;不對每個append加鎖

          3.鎖消除 逃逸分析的數(shù)據(jù)的支持

                  編譯器判斷到一段代碼中,堆上的數(shù)據(jù)不會逃逸出當(dāng)前線程,可以認(rèn)為是線程安全的,不必加鎖

          4.自旋與自適應(yīng)自旋:想要獲取鎖的線程做幾個空循環(huán) 10 CAS實(shí)現(xiàn)

          .為什么引入:

          輕量級鎖失敗后,線程會在操作系統(tǒng)層面掛起

          操作系統(tǒng)實(shí)現(xiàn)線程之間的切換時,需要從用戶態(tài)轉(zhuǎn)換到核心態(tài),狀態(tài)轉(zhuǎn)換耗時

          .解決方法:

          當(dāng)線程在獲取輕量級鎖時CAS操作失敗時,通過自旋讓線程等待,避免線程切換的開銷

          假設(shè)不久當(dāng)前的線程可以獲得鎖,虛擬機(jī)會讓當(dāng)前想要獲取鎖的線程做幾個空循環(huán),可能是50個循環(huán)或100循環(huán)

          結(jié)果:

          如果得到鎖,就順利進(jìn)入臨界區(qū);如果不能,就將線程在操作系統(tǒng)層面掛起,升級為重量級鎖

          自旋鎖的優(yōu)化:自適應(yīng)自旋

          自旋是需要消耗CPU的,如果一直獲取不到鎖,線程一直自旋,浪費(fèi)CPU資源

          線程如果自旋成功了,下次自旋的次數(shù)會更多,自旋失敗了,自旋的次數(shù)就會減少。

          CAS底層實(shí)現(xiàn)原理    用于更新數(shù)據(jù)

          CAS:Compare and Swap, 翻譯成比較并交換 

          CAS需要在:操作值的時候,檢查值有沒有發(fā)生變化,如果沒有發(fā)生變化則更新,最終都會返回內(nèi)存地址,且是原子操作

          需要3個操作數(shù):內(nèi)存地址V,舊預(yù)期值A(chǔ)、新值B

          當(dāng)且僅當(dāng)V符合預(yù)期值A(chǔ)時(即V存儲的值無變化),用B更新A,否則不執(zhí)行更新,最終都返回內(nèi)存地址V

          適用場景:

          1. 適合資源競爭較少的情況,使用synchronized同步鎖進(jìn)行線程阻塞和喚醒切換以及用戶態(tài)內(nèi)核態(tài)間的切換操作額外浪費(fèi)消耗cpu資源;

          2. CAS基于硬件實(shí)現(xiàn),不需要進(jìn)入內(nèi)核,不需要切換線程,操作自旋幾率較少,可以獲得更高的性能

          3. synchronized在jdk1.6之后,已經(jīng)改進(jìn)優(yōu)化。synchronized的底層實(shí)現(xiàn)主要依靠Lock-Free的隊列,基本思路是自旋后阻塞,競爭切換后繼續(xù)競爭鎖,稍微犧牲了公平性,但獲得了高吞吐量。在線程沖突較少的情況下,可以獲得和CAS類似的性能;而線程沖突嚴(yán)重的情況下,性能遠(yuǎn)高于CAS

          4. 原子類中都使用到了CAS 

          volatile詳解

          非可見性:

          編譯器為了加快程序運(yùn)行的速度,對一些變量的寫操作會先在(工作內(nèi)存)寄存器或者是CPU緩存上進(jìn)行,最后才寫入內(nèi)存,這個過程中,變量的新值對其他線程是不可見的

          1.實(shí)現(xiàn)對所有線程的可見性 SMP:對稱多處理器架構(gòu)通過總線BUS進(jìn)行 Cache一致性流量 通信

          1. volatile保證新值立即同步到主存,

          2. 線程對變量讀取的時候,要從主內(nèi)存中讀,而不是緩存

          3. 變量的賦值一旦變化就會通知到其他線程,如果其他線程的工作內(nèi)存中存在這個同一個變量拷貝副本,那么其他線程會放棄這個副本中變量的值,重新去主內(nèi)存中獲取

          適用場景:

          確保只有單一的線程修改變量的值 或 運(yùn)算結(jié)果不依賴當(dāng)前變量值(i++時,運(yùn)算結(jié)果依賴當(dāng)前變量,且是多個線程改變)

          變量不需要與其他的狀態(tài)變量共同參與不變約束

          2.實(shí)現(xiàn) 禁止指令重排序優(yōu)化 應(yīng)用:雙邊檢查單例

          1. 為了減少CPU空閑時間,java不能保證程序執(zhí)行的順序與代碼中一致,

          2. volatile修飾的變量相當(dāng)于生成內(nèi)存屏障,重排序時不能把后面的指令排到屏障之前;指令屏障

          3. 作用:為了保證happen-before原則:寫、鎖lock、傳遞性、線程啟動、中斷、終結(jié)、對象創(chuàng)建的先后關(guān)系

          4. 定義了線程、鎖、volatile變量、對象創(chuàng)建的先后關(guān)系

          5. 若滿足,則保證一個操作執(zhí)行的結(jié)果需要對另一個操作可見

          6. 判斷數(shù)據(jù)是否存在競爭、線程是否安全的依據(jù)

          內(nèi)存屏障實(shí)現(xiàn)方式;volatile的內(nèi)存語義

          編譯器在生成字節(jié)碼時,會在指令序列中,插入內(nèi)存屏障來禁止特定類型的處理器重排序;

          寫前后、讀后后;

          寫寫【寫上下】寫讀、【讀后面】讀讀、讀寫

          1.volatile寫操作的前面插入一個StoreStore屏障:禁止上面的寫

          2.volatile寫操作的后面插入一個SotreLoad屏障:禁止下面的讀

          3.volatile讀操作的后面插入一個LoadLoad屏障:禁止下面的讀

          4.volatile讀操作的后面插入一個LoadStore屏障:禁止下面的寫

            

          3.無法實(shí)現(xiàn) i++ 原子操作

          A讀取 i 后,B也讀取 i ,此時A進(jìn)行 +1,B的 i 就變了

          原子類如何解決:CAS,B在進(jìn)行+1時,檢查此時的 i 跟主存的 i 是否一致,一致才+1;原子類

          synchronized和volatile區(qū)別 鎖的目標(biāo):關(guān)注互斥性和可見性

          1.鎖提供了兩種主要特性:互斥性(mutual exclusion) 和可見性(visibility)。

            互斥即一次只允許一個線程持有某個鎖,使用該共享數(shù)據(jù)。

            可見性確保新值立即同步到主存,每次使用前立即從主內(nèi)存刷新

          2.概念

          synchronized同步阻塞:釋放鎖之前會將對變量的修改刷新到主存當(dāng)中;

          volatile關(guān)鍵字非阻塞:確保新值立即同步到主存,其他線程每次使用前立即從主內(nèi)存刷新;

          3.區(qū)別

          1)volatile非阻塞,synchronized只有當(dāng)前線程可以訪問修飾的變量,其他線程阻塞

          2)volatile僅能修飾變量,synchronized則可以使用在變量,方法.

          3)volatile僅能實(shí)現(xiàn)變量的修改可見性,而synchronized則可以保證變量的修改可見性和原子性(操作不可分割)

          可重入鎖 Re entrantlock

          顯示鎖:基于JDK API、AQS、樂觀鎖實(shí)現(xiàn)、需要顯式的加鎖以及釋放鎖

          1. 無阻塞的同步機(jī)制(非公平鎖實(shí)現(xiàn))

          2. 可實(shí)現(xiàn)輪詢鎖、定時鎖、可中斷鎖特性;

          3. 提供了一個Condition(條件)類,對鎖進(jìn)行更精確的控制

          4. 默認(rèn)使用非公平鎖,可插隊跳過對線程隊列的處理(因此被稱為可重入)

            1. ReentrantLock的內(nèi)部類Sync繼承了AQS,分為公平鎖FairSync和非公平鎖NonfairSync。

            2. 公平鎖:線程獲取鎖的順序和調(diào)用lock的順序一樣,F(xiàn)IFO;喚醒鎖的時間CPU浪費(fèi);是否是AQS隊列中的頭結(jié)點(diǎn)

            3. 非公平鎖:線程獲取鎖的順序和調(diào)用lock的順序無關(guān),先執(zhí)行l(wèi)ock方法的鎖不一定先獲得鎖

          5. 加鎖和解鎖都需要顯式寫出,實(shí)現(xiàn)了Lock接口,注意一定要在適當(dāng)時候unlock

          6. 總結(jié):公平鎖與非公平鎖對比

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

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

          公平鎖的核心

          1. 獲取一次鎖數(shù)量,state值

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

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

          非公平鎖 兩者都是非公平鎖

          1. 非公平鎖,可以直接插隊獲取鎖,跳過了對隊列的處理,速度會更快

            1. 公平鎖為了保證線程規(guī)規(guī)矩矩地排隊,需要增加阻塞和喚醒的時間開銷;

          2. AQS底層原理:在lock獲取鎖時首先判斷當(dāng)前鎖是否可以用(AQS的state狀態(tài)值是否為0),如果是 直接“插隊”獲取鎖,否則進(jìn)入排隊隊列,并阻塞當(dāng)前線程;充分利用了喚醒線程的時間【Singel標(biāo)志喚醒,需要前驅(qū)節(jié)點(diǎn)喚醒】 

          非公平鎖加鎖的簡單步驟

          基于CAS嘗試將state(鎖數(shù)量)從0設(shè)置為1 ---第一次插隊

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

          2. 如果設(shè)置失敗,還會再獲取一次鎖數(shù)量,---第二次插隊

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

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

          3. 入隊后,無限循環(huán)tryAcquire(1)方法 ---第三次插隊 

          非公平鎖源碼

          1.基于CAS將state(鎖數(shù)量)從0設(shè)置為1,如果設(shè)置成功,設(shè)置當(dāng)前線程為獨(dú)占鎖的線程;-->第一次插隊

          若失敗,調(diào)用acquire(1)->tryAcquire(1),acquireQueued(addWaiter(Node.EXCLUSIVE)

          public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt();//請求鎖成功,中斷自己 }

          tryAcquire(arg)會調(diào)用nonfairTryAcquire(1)調(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)下沒有線程在使用 if (compareAndSetState(0, acquires)) {//繼續(xù)通過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; }

          若請求鎖失敗,將當(dāng)前線程鏈入隊尾并掛起,之后等待被喚醒 【快速、正?!?/span>

          1. 首先會使用addWaiter(Node.EXCLUSIVE)將當(dāng)前線程封裝進(jìn)Node節(jié)點(diǎn)node,然后將該節(jié)點(diǎn)加入等待隊列

          2. 先快速入隊【存在尾節(jié)點(diǎn),將使用CAS嘗試將尾節(jié)點(diǎn)設(shè)置為node】

          3. 如果快速入隊不成功【尾節(jié)點(diǎn)為空】,使用正常入隊方法enq,無限循環(huán)=第一次阻塞,直到Node節(jié)點(diǎn)入隊為止【創(chuàng)建一個dummy節(jié)點(diǎn),并將該節(jié)點(diǎn)通過CAS設(shè)置到頭節(jié)點(diǎn),若頭結(jié)點(diǎn)不為null,cas繼續(xù)快速入隊】

          入隊成功后返回node節(jié)點(diǎn),繼續(xù)第三次插隊

          無限循環(huán)調(diào)用:acquireQueued(final Node node, int arg)獲取node的前驅(qū)節(jié)點(diǎn)p

          p==head&&tryAcquire(1) 是唯一跳出循環(huán)的方法:p成為頭結(jié)點(diǎn)并且獲取鎖成功:如果p是頭節(jié)點(diǎn),就繼續(xù)使用tryAcquire(1)方法插隊,若成功,不用中斷,第三次插隊成功

          1. 如果p不是頭節(jié)點(diǎn),或者tryAcquire(1)請求不成功,執(zhí)行shouldParkAfterFailedAcquire(Node pred, Node node)來檢測當(dāng)前節(jié)點(diǎn)是不是可以安全的被掛起:判斷p的等待狀態(tài)waitStatus

            1. SIGNAL(即可以喚醒下一個節(jié)點(diǎn)的線程),則node節(jié)點(diǎn)的線程可以安全掛起,返回true

            2. CANCELLED,則p的線程被取消了,我們會將p之前的連續(xù)幾個被取消的前驅(qū)節(jié)點(diǎn)從隊列中剔除

            3. 等待狀態(tài)是除了上述兩種的其他狀態(tài),CAS嘗試將前驅(qū)節(jié)點(diǎn)的等待狀態(tài)設(shè)為SIGNAL【p與node競爭】

          掛起后后 跳出循環(huán),需要中斷自身

          LockSupport.park(this);//掛起當(dāng)前的線程,后等待前去節(jié)點(diǎn)unpark喚醒該線程;方法為public

           

          DelayQueue中的使用示例:

          1. take()和offer()都是lock了重入鎖,按照synchronized的公平鎖,兩個方法是互斥

          2. take()方法需要等待1個小時才能返回,offer()需要馬上提交一個10秒后運(yùn)行的任務(wù),此時offer()可以插隊獲取鎖

          3. 原理:A執(zhí)行時,B lock()鎖,并休眠;當(dāng)鎖被A釋放處于可用狀態(tài)時,B線程卻還處于被喚醒的過程中,此時C線程請求鎖,可以優(yōu)先C得到鎖

           

          Reentrantlock優(yōu)點(diǎn)

          1. 顯示鎖可中斷,防止死鎖,內(nèi)置鎖不可中斷,會產(chǎn)生死鎖

          2. 實(shí)現(xiàn)其他特性的鎖

          3. 對鎖更精細(xì)的控制

           

          synchronized優(yōu)點(diǎn)

          1. 顯示鎖易忘記 finally 塊釋放鎖,對程序有害

          2. 顯示鎖只能用在代碼塊,強(qiáng)制更細(xì)粒度的加鎖;syn可以用在方法上

          3. synchronized 管理鎖定和釋放時,能標(biāo)識死鎖或者其他異常行為的來源,利于調(diào)試

          4. Synchronized引入了偏向鎖,輕量級鎖(自旋鎖)后,兩者的性能就差不多

          使用場景

          1. Condition類對鎖進(jìn)行更精確的控制,指定喚醒、分組喚醒

          2. 防止死鎖

          3. 輪詢鎖:用tryLock(long timeout, TimeUnit unit)和tryLock() 這兩個方法實(shí)現(xiàn),即沒有獲取到鎖,可以使用while循環(huán) 隔一段時間再次獲取,直到獲取到為止

          4. 定時鎖:指的是在指定時間內(nèi)沒有獲取到鎖,就取消阻塞并返回獲取鎖失??;tryLock(long timeout, TimeUnit unit)

          5. 可中斷鎖:lockInterruptibly,防止死鎖 

          區(qū)別

          synchronied是JVM級別的,而ReentrantLock是api級別的

          JVM會對synchronied做出相應(yīng)的優(yōu)化

          鎖消除:JVM判斷堆上的數(shù)據(jù)不會逃逸出當(dāng)前線程,不加鎖;

          自旋鎖

          自適應(yīng)鎖

           

          提供的lock()方法:

          重入鎖實(shí)現(xiàn) 直到state為0,其他鎖才可以用

          1. 如果該鎖沒有被另一個線程持有,則獲取該鎖并立即返回,將鎖計數(shù)設(shè)置為 1;對應(yīng)AQS中的state

          2. 如果當(dāng)前線程已經(jīng)持有該鎖,將鎖計數(shù)加 1,并立即返回方法---重入鎖

          3. 如果該鎖被另一個線程持有,則禁用當(dāng)前線程,在獲得鎖之前,一直休眠,此時鎖保持計數(shù)設(shè)置為 1

          排他鎖實(shí)現(xiàn):Lock類有讀鎖和寫鎖,讀讀共享,寫寫互斥,讀寫互斥:每次獲取鎖時都是首先判斷state是否為0,并且只有1個線程能獲取到鎖

          tryLock和lock和lockInterruptibly的區(qū)別

          1. tryLock能獲得鎖就返回true,不能就立即返回false,可以增加時間限制,如果超過該時間段還沒獲得鎖,返回false;tryLock(long timeout,TimeUnit unit),

          2. lock能獲得鎖就返回true,不能的話一直等待獲得鎖

          3. lockInterruptibly,中斷會拋出異常 

          鎖的Condition類

          Lock類可以創(chuàng)建Condition對象,Condition對象用來是線程等待和喚醒線程;Condition condition=lock.newCondition();

          對鎖進(jìn)行更精確的控制

          1. Condition中的await()方法相當(dāng)于Object的wait()方法

          2. Condition中的signal()方法相當(dāng)于Object的notify()方法

          3. Condition中的signalAll()相當(dāng)于Object的notifyAll()方法

          4. ReentrantLock類可以喚醒指定條件的線程,而object的喚醒是隨機(jī)的 

          Condition函數(shù)列表

          1. 造成當(dāng)前線程在接到信號或被中斷之前一直處于等待狀態(tài) void await()

          2. 喚醒一個等待線程 void signal()

          3. 喚醒所有等待線程 void signalAll()

          4. 造成當(dāng)前線程在接到信號、被中斷或到達(dá)指定等待時間之前一直處于等待狀態(tài) boolean await(long time, TimeUnit unit)

          5. 造成當(dāng)前線程在接到信號、被中斷或到達(dá)指定等待時間之前一直處于等待狀態(tài) long awaitNanos(long nanosTimeout)

          6. 造成當(dāng)前線程在接到信號之前一直處于等待狀態(tài) void awaitUninterruptibly()

          7. 造成當(dāng)前線程在接到信號、被中斷或到達(dá)指定最后期限之前一直處于等待狀態(tài) boolean awaitUntil(Date deadline) 

          共享鎖實(shí)現(xiàn) 并發(fā)讀 ReentrantReadWriteLock、計數(shù)器

          共享鎖的AQS實(shí)現(xiàn)

          實(shí)現(xiàn)tryAcquireShared方法【檢查下一個節(jié)點(diǎn)是共享節(jié)點(diǎn)】,獲取共享鎖,鎖在所有調(diào)用await方法的線程間共享

          底層:

          1. 在AQS隊列中,將線程包裝為Node.SHARED節(jié)點(diǎn),即標(biāo)志為共享鎖

          2. 當(dāng)頭節(jié)點(diǎn)獲得共享鎖后,喚醒下一個共享類型結(jié)點(diǎn)的操作


            1. 頭節(jié)點(diǎn)node1調(diào)用unparkSuccessor()方法喚醒了Node2,并且調(diào)用tryAcquireShared方法檢查下一個節(jié)點(diǎn)是共享節(jié)點(diǎn)

            2. 如果是,更改頭結(jié)點(diǎn),重復(fù)以上步驟,以實(shí)現(xiàn)節(jié)點(diǎn)自身獲取共享鎖成功后,喚醒下一個共享類型結(jié)點(diǎn)的操作

          應(yīng)用:

          1.ReentrantReadWriteLock

          2.CountDownLatch為java.util.concurrent包下的計數(shù)器工具類

          可被多線程并發(fā)的實(shí)現(xiàn)減1操作,并在計數(shù)器為0后,調(diào)用await方法的線程被喚醒,從而實(shí)現(xiàn)多線程間的協(xié)作

          new CountDownLatch(3).countDown();

          用來實(shí)現(xiàn)等所有共享鎖線程都喚醒后一起協(xié)作 

          死鎖

          多個線程因競爭資源而造成僵局(互相等待),無法向前推進(jìn)

          產(chǎn)生的原因

          1) 系統(tǒng)資源的競爭

          系統(tǒng)不可剝奪資源,數(shù)量不足以滿足多個進(jìn)程運(yùn)行,使得進(jìn)程在運(yùn)行過程中,因競爭資源而陷入僵局

          2) 進(jìn)程推進(jìn)順序非法

          請求和釋放資源的順序不當(dāng),也同樣會導(dǎo)致死鎖。如,互相申請各占有的資源。

          信號量使用不當(dāng)也會造成死鎖。進(jìn)程間彼此相互等待消息,結(jié)果也會使得這 些進(jìn)程間無法繼續(xù)向前推進(jìn)。

          3) 死鎖產(chǎn)生的4個必要條件,只要其中任一條件不成立,死鎖就不會發(fā)生

          1. 資源互斥條件:資源互斥,即某資源僅為一個進(jìn)程占有

          2. 資源不可剝奪條件:進(jìn)程所獲得的資源在未使用完畢之前,只能是主動釋放,不能被其他進(jìn)程強(qiáng)行奪走

          3. 保持和請求條件:進(jìn)程已經(jīng)保持了一個資源,又提出了新的資源請求,而該資源已被其他進(jìn)程占有

          4. 循環(huán)等待條件:進(jìn)程資源循環(huán)等待 

          如何避免死鎖

          1. 加鎖順序(線程按照一定的順序加鎖)

            1. 按照順序加鎖是一種死鎖預(yù)防機(jī)制,需要事先知道所有會用到的鎖

          2. 加鎖時限(超時則放棄)

            1. 獲取鎖時加上時限,超過時限則放棄請求,并釋放鎖,等待一段隨機(jī)的時間再重試

          3. 死鎖檢測與恢復(fù)

            1. 操作系統(tǒng)中:系統(tǒng)為進(jìn)程分配資源,不采取任何限制性措施,提供檢測和恢復(fù)的手段

          死鎖檢測:當(dāng)一個線程請求鎖失敗時,遍歷鎖的關(guān)系圖檢測死鎖

          死鎖恢復(fù)

          1. 撤消進(jìn)程,剝奪資源

          2. 線程設(shè)置優(yōu)先級,讓一個(或幾個)線程回退,剩下的線程就像沒發(fā)生死鎖一樣繼續(xù)保持著它們需要的鎖

          3. 死鎖發(fā)生的時候設(shè)置隨機(jī)的優(yōu)先級;如果賦予這些線程的優(yōu)先級是固定不變的,同一批線程總是會擁有更高的優(yōu)先級。

          鎖模式包括: 

          1. 共享鎖:(讀?。┯脩艨梢圆l(fā)讀取數(shù)據(jù),但不能獲取寫鎖,直到釋放所有讀鎖。

          2. 排他鎖(寫鎖):加上寫鎖后,其他線程無法加任何鎖;寫鎖可以讀和寫

          3. 更新鎖: 防止死鎖而設(shè)立,轉(zhuǎn)換讀鎖為寫鎖之前的準(zhǔn)備,僅一個線程可獲得更新鎖

          樂觀鎖:認(rèn)為數(shù)據(jù)一般情況下不會造成沖突,在數(shù)據(jù)提交更新時,才進(jìn)行數(shù)據(jù)的沖突檢測;

          如果沖突,返回信息讓用戶決定如何去做。實(shí)現(xiàn)方式:記錄數(shù)據(jù)版本。

          悲觀鎖:操作數(shù)據(jù)時上鎖保護(hù),限制其他線程訪問,直到該鎖釋放。關(guān)系型數(shù)據(jù)庫鎖機(jī)制,行鎖、頁鎖、表鎖,都是在做操作之前先上鎖。

          鎖的粒度:都是悲觀鎖

          1. 行鎖: 粒度最小,并發(fā)性最高

          2. 頁鎖:鎖定一頁。25個行鎖可升級為一個頁鎖。

          3. 表鎖:粒度大,并發(fā)性低

          4. 數(shù)據(jù)庫鎖:控制整個數(shù)據(jù)庫操作 

          Happen-Before原則 八大原則:

          1. 定義了線程、鎖、volatile變量、對象創(chuàng)建的先后關(guān)系

          2. 若滿足,則保證一個操作執(zhí)行的結(jié)果需要對另一個操作可見

          3. 判斷數(shù)據(jù)是否存在競爭、線程是否安全的依據(jù)

          • 單線程:在同一個線程中,書寫在前面的操作happen-before后面的操作。

          • 鎖:解鎖先于鎖定;同一個鎖的unlock操作happen-before此鎖的lock操作。

          • volatile:先寫;對一個volatile變量的寫操作happen-before對此變量的任意操作(當(dāng)然也包括寫操作了)。

          • 傳遞性原則:如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。

          • 線程啟動:start方法優(yōu)先;同一個線程的start方法happen-before此線程的其它方法。

          • 線程中斷:對線程interrupt方法的調(diào)用happen-before被中斷線程的檢測到中斷發(fā)送的代碼。

          • 線程終結(jié):線程中的所有操作都happen-before線程的終止檢測。

          • 對象創(chuàng)建:先初始化,后finalize;一個對象的初始化完成先于他的finalize方法調(diào)用。

           



          版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明。

          本文鏈接:

          https://blog.csdn.net/zzpueye/article/details/90047506






          鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布

          ??????

          ??長按上方微信二維碼 2 秒





          感謝點(diǎn)贊支持下哈 

          瀏覽 60
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  超碰97国产 | 成人爆操视频 | 欧美美穴一区 | 亚洲成人网站视频 | 91成人精品在线视频 |