Java中鎖的概念

在談鎖之前,首先要理解一些常用的鎖的分類名詞:

1. 鎖的常用分類

  • 死鎖:所謂死鎖沛厨,就是多個線程同時被阻塞系馆,它們中的一個或者全部都在等待某個資源被釋放痴突。
    比如:線程A持有對象b的鎖,等待獲取對象a的鎖狼荞;線程B持有對象a的鎖辽装,同時又在等待獲取對象b的鎖。 這種情形下相味,線程A和線程B在互相等待拾积,永遠(yuǎn)無法獲取對方持有的鎖,同時又不是釋放已經(jīng)持有的鎖攻走,就形成了死鎖殷勘。

1.1 樂觀鎖和悲觀鎖

樂觀鎖與悲觀鎖是一種廣義上的概念,體現(xiàn)了看待線程同步的不同角度昔搂。在Java和數(shù)據(jù)庫中都有此概念對應(yīng)實際應(yīng)用玲销。

  • 悲觀鎖:對于同一個數(shù)據(jù)的并發(fā)操作,悲觀鎖認(rèn)為自己在使用數(shù)據(jù)的時候一定有別的線程來修改數(shù)據(jù)摘符,因此在獲取數(shù)據(jù)的時候會先加鎖贤斜,確保數(shù)據(jù)不會被別的線程修改。Java中逛裤,synchronized關(guān)鍵字和Lock的實現(xiàn)類都是悲觀鎖瘩绒。
  • 樂觀鎖: 樂觀鎖認(rèn)為自己在使用數(shù)據(jù)時不會有別的線程修改數(shù)據(jù),所以不會添加鎖带族,只是在更新數(shù)據(jù)的時候去判斷之前有沒有別的線程更新了這個數(shù)據(jù)锁荔。如果這個數(shù)據(jù)沒有被更新,當(dāng)前線程將自己修改的數(shù)據(jù)成功寫入蝙砌。如果數(shù)據(jù)已經(jīng)被其他線程更新阳堕,則根據(jù)不同的實現(xiàn)方式執(zhí)行不同的操作(例如報錯或者自動重試)。
    Java 中的樂觀鎖基本都是通過 CAS(Compare And Swap择克,比較交換)操作實現(xiàn)的恬总,CAS 是一種更新的原子操作,比較當(dāng)前值(版本標(biāo)識)跟傳入值(版本標(biāo)識)是否一樣肚邢,一樣則更新壹堰,否則失敗,放棄更新骡湖。

CAS
在JDK1.5之前都是靠synchronized關(guān)鍵字保證同步贱纠,synchronized保證了無論哪個線程持有共享變量的鎖,都會采用獨占的方式來訪問這些變量响蕴。這種情況下:
1.在多線程競爭下并巍,加鎖、釋放鎖會導(dǎo)致較多的上下文切換和調(diào)度延時换途,引起性能問題
2.如果一個線程持有鎖懊渡,其他的線程就都會掛起刽射,等待持有鎖的線程釋放鎖。
3.如果一個優(yōu)先級高的線程等待一個優(yōu)先級低的線程釋放鎖剃执,會導(dǎo)致優(yōu)先級倒置誓禁,引起性能風(fēng)險。
在JDK1.5新增的java.util.concurrent(JUC java并發(fā)工具包)就是建立在CAS之上的肾档。相比于synchronized這種堵塞算法摹恰,CAS是非堵塞算法的一種常見實現(xiàn)。所以JUC在性能上有了很大的提升怒见。
CASvolatile關(guān)鍵字是實現(xiàn)并發(fā)包的基石俗慈。沒有CAS就不會有并發(fā)包,synchronized是一種獨占鎖遣耍、悲觀鎖闺阱,java.util.concurrent中借助了CAS指令實現(xiàn)了一種區(qū)別于synchronized的一種樂觀鎖。

CAS操作包括三個操作數(shù):需要讀寫的內(nèi)存位置(V)舵变、預(yù)期原值(A)酣溃、新值(B)。如果內(nèi)存位置與預(yù)期原值的A相匹配纪隙,那么將內(nèi)存位置的值更新為新值B狡逢。如果內(nèi)存位置與預(yù)期原值的值不匹配诀蓉,那么處理器不會做任何操作。無論哪種情況扰肌,它都會在 CAS 指令之前返回該位置的值袜刷。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功贬循,而不提取當(dāng)前值郊尝。)CAS其實就是一個:我認(rèn)為位置 V 應(yīng)該包含值 A特笋;如果包含該值,則將 B 放到這個位置拢切;否則,不要更改該位置秆吵,只告訴我這個位置現(xiàn)在的值即可淮椰。這其實和樂觀鎖的沖突檢測+數(shù)據(jù)更新的原理是一樣的。

我們以AtomicInteger類為例纳寂,簡單看一下實現(xiàn)過程:

AtomicInteger.png

用到了Unsafe類主穗,valuevolatile修飾的。Unsafe類中方法 大部分是native的毙芜,是用C++寫的跟底層硬件CPU指令通訊的復(fù)制工具類忽媒。

CAS缺陷:

  1. ABA問題
    CAS需要在操作值的時候檢查內(nèi)存值是否發(fā)生變化,沒有發(fā)生變化才會更新內(nèi)存值腋粥。但是如果內(nèi)存值原來是A晦雨,后來變成了B架曹,然后又變成了A,那么CAS進(jìn)行檢查時會發(fā)現(xiàn)值沒有發(fā)生變化闹瞧,但是實際上是有變化的绑雄。ABA問題的解決思路就是在變量前面添加版本號,每次變量更新的時候都把版本號加一奥邮,這樣變化過程就從“A-B-A”變成了“1A-2B-3A”万牺。
    JDK從1.5開始提供了AtomicStampedReference類來解決ABA問題,具體操作封裝在compareAndSet()中洽腺。compareAndSet()首先檢查當(dāng)前引用和當(dāng)前標(biāo)志與預(yù)期引用和預(yù)期標(biāo)志是否相等脚粟,如果都相等,則以原子方式將引用值和標(biāo)志的值設(shè)置為給定的更新值蘸朋。
  2. 循環(huán)時間長開銷大
    自旋CAS(不成功核无,就一直循環(huán)執(zhí)行,直到成功)如果長時間不成功度液,會給CPU帶來非常大的執(zhí)行開銷厕宗。如果JVM能支持處理器提供的pause指令那么效率會有一定的提升,pause指令有兩個作用堕担,第一它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會消耗過多的執(zhí)行資源已慢,延遲的時間取決于具體實現(xiàn)的版本,在一些處理器上延遲時間是零霹购。第二它可以避免在退出循環(huán)的時候因內(nèi)存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush)佑惠,從而提高CPU的執(zhí)行效率。
    3.只能保證一個共享變量的原子操作
    當(dāng)對一個共享變量執(zhí)行操作時齐疙,我們可以使用循環(huán)CAS的方式來保證原子操作膜楷,但是對多個共享變量操作時,循環(huán)CAS就無法保證操作的原子性贞奋,這個時候就可以用鎖赌厅,或者有一個取巧的辦法,就是把多個共享變量合并成一個共享變量來操作轿塔。比如有兩個共享變量i=2,j=a特愿,合并一下ij=2a,然后用CAS來操作ij勾缭。從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性揍障,你可以把多個變量放在一個對象里來進(jìn)行CAS操作。

1.2 自旋鎖 和 適應(yīng)性自旋鎖

  • 自旋鎖的應(yīng)用場景
    阻塞或喚醒一個Java線程需要操作系統(tǒng)切換CPU狀態(tài)來完成俩由,這種狀態(tài)轉(zhuǎn)換需要耗費處理器時間毒嫡。如果同步代碼塊中的內(nèi)容過于簡單,新開線程狀態(tài)轉(zhuǎn)換消耗的時間有可能比同步代碼塊執(zhí)行的時間還要長幻梯。
    在許多場景中兜畸,同步資源的鎖定時間很短努释,為了這一小段時間去切換線程,線程掛起和恢復(fù)現(xiàn)場的花費可能會讓系統(tǒng)得不償失膳叨。如果物理機(jī)器有多個處理器洽洁,能夠讓兩個或以上的線程同時并行執(zhí)行,我們就可以讓后面那個請求鎖的線程不放棄CPU的執(zhí)行時間菲嘴,看看持有鎖的線程是否很快就會釋放鎖饿自。

而為了讓當(dāng)前線程“稍等一下”,我們需讓當(dāng)前線程進(jìn)行自旋龄坪,如果在自旋完成后前面鎖定同步資源的線程已經(jīng)釋放了鎖昭雌,那么當(dāng)前線程就可以不必阻塞而是直接獲取同步資源,從而避免切換線程的開銷健田。這就是自旋鎖烛卧。
自旋鎖的實現(xiàn)原理同樣也是CAS,AtomicInteger中調(diào)用unsafe進(jìn)行自增操作的源碼中的do-while循環(huán)就是一個自旋操作妓局,如果修改數(shù)值失敗則通過循環(huán)來執(zhí)行自旋总放,直至修改成功。

getAndAddInt.png

  • 自旋鎖的優(yōu)缺點
    自旋鎖盡可能的減少線程的阻塞好爬,這對于鎖的競爭不激烈局雄,且占用鎖時間非常短的代碼塊來說性能能大幅度的提升,因為自旋的消耗會小于線程阻塞掛起再喚醒的操作的消耗存炮!
    自旋鎖本身是有缺點的炬搭,它不能代替阻塞。自旋等待雖然避免了線程切換的開銷穆桂,但它要占用處理器時間宫盔。如果鎖被占用的時間很短,自旋等待的效果就會非常好享完。反之灼芭,如果鎖被占用的時間很長,那么自旋的線程只會白浪費處理器資源般又。所以彼绷,自旋等待的時間必須要有一定的限度,如果自旋超過了限定次數(shù)(默認(rèn)是10次倒源,可以使用-XX:PreBlockSpin來更改)沒有成功獲得鎖苛预,就應(yīng)當(dāng)掛起線程句狼。
  • 適應(yīng)性自旋鎖
    因為自旋鎖為了更快的獲取到鎖笋熬,而一直占用CPU,直到獲取鎖或者超過自旋時間腻菇,所以需要確定一個合理的自旋時間胳螟。
    jdk1.5中昔馋,JVM 對于自旋周期的限制是寫死的,在 jdk1.6 引入了適應(yīng)性自旋鎖糖耸,也就意味著自旋的時間不在是固定的了秘遏。 而是由前一次在同一個鎖上的自旋時間以及鎖的擁有者的狀態(tài)來決定,基本認(rèn)為一個線程上下文切換的時間是最佳的一個時間嘉竟。如果在同一個鎖對象上邦危,自旋等待剛剛成功獲得過鎖,并且持有鎖的線程正在運行中舍扰,那么虛擬機(jī)就會認(rèn)為這次自旋也是很有可能再次成功倦蚪,進(jìn)而它將允許自旋等待持續(xù)相對更長的時間。如果對于某個鎖边苹,自旋很少成功獲得過陵且,那在以后嘗試獲取這個鎖時將可能省略掉自旋過程,直接阻塞線程个束,避免浪費處理器資源慕购。
    同時 JVM 還針對當(dāng) 前 CPU 的負(fù)荷情況做了較多的優(yōu)化:
    • 如果平均負(fù)載小于 CPUs 則一直自旋;
    • 如果有超過(CPUs/2)個線程正在自旋茬底,則后來線程直接阻塞沪悲;
    • 如果正在自旋的線程發(fā)現(xiàn) Owner 發(fā)生了變化則延遲自旋時間(自旋計數(shù))或進(jìn)入阻塞;
    • 如果 CPU 處于節(jié)電模式則停止自旋桩警;

1.3 公平鎖 和 非公平鎖

  • 公平鎖 (FairSync
    公平鎖是指多個線程按照申請鎖的順序來獲取鎖可训,線程直接進(jìn)入隊列中排隊,隊列中的第一個線程才能獲得鎖捶枢。公平鎖的優(yōu)點是等待鎖的線程不會餓死握截。缺點是整體吞吐效率相對非公平鎖要低,等待隊列中除第一個線程以外的所有線程都會阻塞烂叔,CPU喚醒阻塞線程的開銷比非公平鎖大谨胞。

  • 非公平鎖(NonfairSync
    非公平鎖是多個線程加鎖時直接嘗試獲取鎖(不用進(jìn)隊列排隊),獲取不到才會到等待隊列的隊尾等待蒜鸡。但如果此時鎖剛好可用胯努,那么這個線程可以無需阻塞直接獲取到鎖,所以非公平鎖有可能出現(xiàn)后申請鎖的線程先獲取鎖的場景逢防。非公平鎖的優(yōu)點是可以減少喚起線程的開銷叶沛,整體的吞吐效率高,因為線程有幾率不阻塞直接獲得鎖忘朝,CPU不必喚醒所有線程灰署。缺點是處于等待隊列中的線程可能會餓死,或者等很久才會獲得鎖。

    • 非公平鎖性能比公平鎖高 5~10 倍溉箕,因為公平鎖需要在多核的情況下維護(hù)一個隊列晦墙;
    • Java 中的synchronized是非公平鎖,ReentrantLock默認(rèn)的lock()方法采用的是非公平鎖肴茄。

1.4 可重入鎖 和 非可重入鎖

  • 可重入鎖
    可重入鎖又名遞歸鎖晌畅,是指在同一個線程在外層方法獲取鎖的時候,再進(jìn)入該方法的內(nèi)層方法會自動獲取鎖(前提鎖對象得是同一個對象或者class)寡痰,不會因為之前已經(jīng)獲取過還沒釋放而阻塞抗楔。Java中ReentrantLocksynchronized都是可重入鎖,可重入鎖的一個優(yōu)點是可一定程度避免死鎖拦坠。
  • 可重入鎖和非可重入鎖流程區(qū)別
    對比可重入鎖ReentrantLock和非可重入鎖NonReentrantLock可發(fā)現(xiàn)流程的區(qū)別:
    首先ReentrantLockNonReentrantLock都繼承父類AQS(AbstractQueuedSynchronizer)谓谦,其父類AQS中維護(hù)了一個同步狀態(tài)status來計數(shù)重入次數(shù),status初始值為0贪婉。
    當(dāng)線程嘗試獲取鎖時反粥,可重入鎖先嘗試獲取并更新status值,如果status == 0表示沒有其他線程在執(zhí)行同步代碼疲迂,則把status置為1才顿,當(dāng)前線程開始執(zhí)行。如果status != 0尤蒿,則判斷當(dāng)前線程是否是獲取到這個鎖的線程郑气,如果是的話執(zhí)行status+1,且當(dāng)前線程可以再次獲取鎖腰池。而非可重入鎖是直接去獲取并嘗試更新當(dāng)前status的值尾组,如果status != 0的話會導(dǎo)致其獲取鎖失敗,當(dāng)前線程阻塞示弓。
    釋放鎖時讳侨,可重入鎖同樣先獲取當(dāng)前status的值,在當(dāng)前線程是持有鎖的線程的前提下奏属。如果status-1 == 0跨跨,則表示當(dāng)前線程所有重復(fù)獲取鎖的操作都已經(jīng)執(zhí)行完畢,然后該線程才會真正釋放鎖囱皿。而非可重入鎖則是在確定當(dāng)前線程是持有鎖的線程之后勇婴,直接將status置為0,將鎖釋放嘱腥。

1.5 共享鎖和獨占鎖

java 并發(fā)包提供的加鎖模式分為獨占鎖和共享鎖耕渴。

  • 獨占鎖
    獨享鎖也叫排他鎖,是指該鎖一次只能被一個線程所持有齿兔。如果線程T對數(shù)據(jù)A加上排它鎖后橱脸,則其他線程不能再對A加任何類型的鎖窄做。獲得排它鎖的線程即能讀數(shù)據(jù)又能修改數(shù)據(jù)。JDK中的synchronized和JUC中Lock的實現(xiàn)類就是互斥鎖慰技。獨占鎖是一種悲觀保守的加鎖策略,避免了讀/讀沖突组砚,如果某個只讀線程獲取鎖吻商,則其他讀線 程都只能等待,這種情況下就限制了不必要的并發(fā)糟红,因為讀操作不會影響數(shù)據(jù)的一致性艾帐。

  • 共享鎖
    共享鎖是指該鎖可被多個線程所持有。如果線程T對數(shù)據(jù)A加上共享鎖后盆偿,則其他線程只能對A再加共享鎖柒爸,不能加排它鎖。獲得共享鎖的線程只能讀數(shù)據(jù)事扭,不能修改數(shù)據(jù)捎稚。如:ReadWriteLock。 共享鎖則是一種樂觀鎖求橄,它放寬了加鎖策略今野,允許多個執(zhí)行讀操作的線程同時訪問共享資源。

    • AQS 的內(nèi)部類Node定義了兩個常量SHAREDEXCLUSIVE罐农,他們分別標(biāo)識 AQS 隊列中等待線程的鎖獲取模式条霜。
    • Java 的并發(fā)包中提供了ReadWriteLock,讀-寫鎖涵亏。它允許一個資源可以被多個讀操作訪問宰睡,或者被一個寫操作訪問,但兩者不能同時進(jìn)行气筋。

1.6 無鎖 -> 偏向鎖 -> 輕量級鎖 -> 重量級鎖

這四種鎖的狀態(tài)拆内,專門針對synchronized的。
首先為什么Synchronized能實現(xiàn)線程同步宠默?在回答這個問題之前我們需要了解兩個重要的概念:“Java對象頭”矛纹、“Monitor”。
Java對象頭
synchronized是悲觀鎖光稼,在操作同步資源之前需要給同步資源先加鎖或南,這把鎖就是存在Java對象頭里的,而Java對象頭又是什么呢艾君?
我們以Hotspot虛擬機(jī)為例采够,Hotspot的對象頭主要包括兩部分?jǐn)?shù)據(jù):Mark Word(標(biāo)記字段)、Klass Pointer(類型指針)冰垄。
Mark Word:默認(rèn)存儲對象的HashCode蹬癌,分代年齡和鎖標(biāo)志位信息。這些信息都是與對象自身定義無關(guān)的數(shù)據(jù),所以Mark Word被設(shè)計成一個非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲盡量多的數(shù)據(jù)逝薪。它會根據(jù)對象的狀態(tài)復(fù)用自己的存儲空間隅要,也就是說在運行期間Mark Word里存儲的數(shù)據(jù)會隨著鎖標(biāo)志位的變化而變化。
Klass Point:對象指向它的類元數(shù)據(jù)的指針董济,虛擬機(jī)通過這個指針來確定這個對象是哪個類的實例步清。
Monitor
Monitor可以理解為一個同步工具或一種同步機(jī)制,通常被描述為一個對象虏肾。每一個Java對象就有一把看不見的鎖廓啊,稱為內(nèi)部鎖或者M(jìn)onitor鎖。
Monitor是線程私有的數(shù)據(jù)結(jié)構(gòu)封豪,每一個線程都有一個可用monitor record列表谴轮,同時還有一個全局的可用列表。每一個被鎖住的對象都會和一個monitor關(guān)聯(lián)吹埠,同時monitor中有一個Owner字段存放擁有該鎖的線程的唯一標(biāo)識第步,表示該鎖被這個線程占用。
現(xiàn)在話題回到synchronized缘琅,synchronized通過Monitor來實現(xiàn)線程同步雌续,Monitor是依賴于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來實現(xiàn)的線程同步。
如同我們在自旋鎖中提到的“阻塞或喚醒一個Java線程需要操作系統(tǒng)切換CPU狀態(tài)來完成胯杭,這種狀態(tài)轉(zhuǎn)換需要耗費處理器時間驯杜。如果同步代碼塊中的內(nèi)容過于簡單,狀態(tài)轉(zhuǎn)換消耗的時間有可能比用戶代碼執(zhí)行的時間還要長”做个。這種方式就是synchronized最初實現(xiàn)同步的方式鸽心,這就是JDK 6之前synchronized效率低的原因。這種依賴于操作系統(tǒng)Mutex Lock所實現(xiàn)的鎖我們稱之為“重量級鎖”居暖,JDK 6中為了減少獲得鎖和釋放鎖帶來的性能消耗顽频,引入了“偏向鎖”和“輕量級鎖”。
所以目前鎖一共有4種狀態(tài)太闺,級別從低到高依次是:無鎖糯景、偏向鎖、輕量級鎖和重量級鎖省骂。鎖狀態(tài)只能升級不能降級蟀淮。
通過上面的介紹,我們對synchronized的加鎖機(jī)制以及相關(guān)知識有了一個了解钞澳,那么下面我們給出四種鎖狀態(tài)對應(yīng)的的Mark Word內(nèi)容怠惶,然后再分別講解四種鎖狀態(tài)的思路以及特點:

Mark Word.png

  • 無鎖
    無鎖沒有對資源進(jìn)行鎖定,所有的線程都能訪問并修改同一個資源轧粟,但同時只有一個線程能修改成功策治。
    無鎖的特點就是修改操作在循環(huán)內(nèi)進(jìn)行脓魏,線程會不斷的嘗試修改共享資源。如果沒有沖突就修改成功并退出通惫,否則就會繼續(xù)循環(huán)嘗試茂翔。如果有多個線程修改同一個值,必定會有一個線程能修改成功履腋,而其他修改失敗的線程會不斷重試直到修改成功珊燎。上面我們介紹的CAS原理及應(yīng)用即是無鎖的實現(xiàn)。無鎖無法全面代替有鎖府树,但無鎖在某些場合下的性能是非常高的。
  • 偏向鎖
    偏向鎖是指一段同步代碼一直被一個線程所訪問料按,那么該線程會自動獲取鎖奄侠,降低獲取鎖的代價。
    在大多數(shù)情況下载矿,鎖總是由同一線程多次獲得垄潮,不存在多線程競爭,所以出現(xiàn)了偏向鎖闷盔。其目標(biāo)就是在只有一個線程執(zhí)行同步代碼塊時能夠提高性能弯洗。
    當(dāng)一個線程訪問同步代碼塊并獲取鎖時,會在Mark Word里存儲鎖偏向的線程ID逢勾。在線程進(jìn)入和退出同步塊時不再通過CAS操作來加鎖和解鎖牡整,而是檢測Mark Word里是否存儲著指向當(dāng)前線程的偏向鎖。引入偏向鎖是為了在無多線程競爭的情況下盡量減少不必要的輕量級鎖執(zhí)行路徑溺拱,因為輕量級鎖的獲取及釋放依賴多次CAS原子指令逃贝,而偏向鎖只需要在置換ThreadID的時候依賴一次CAS原子指令即可。
    偏向鎖只有遇到其他線程嘗試競爭偏向鎖時迫摔,持有偏向鎖的線程才會釋放鎖沐扳,線程不會主動釋放偏向鎖。偏向鎖的撤銷句占,需要等待全局安全點(在這個時間點上沒有字節(jié)碼正在執(zhí)行)沪摄,它會首先暫停擁有偏向鎖的線程,判斷鎖對象是否處于被鎖定狀態(tài)纱烘。撤銷偏向鎖后恢復(fù)到無鎖(標(biāo)志位為“01”)或輕量級鎖(標(biāo)志位為“00”)的狀態(tài)杨拐。
    偏向鎖在JDK 6及以后的JVM里是默認(rèn)啟用的±奚叮可以通過JVM參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false戏阅,關(guān)閉之后程序默認(rèn)會進(jìn)入輕量級鎖狀態(tài)。
  • 輕量級鎖
    是指當(dāng)鎖是偏向鎖的時候啤它,被另外的線程所訪問奕筐,偏向鎖就會升級為輕量級鎖舱痘,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞离赫,從而提高性能芭逝。
    在代碼進(jìn)入同步塊的時候,如果同步對象鎖狀態(tài)為無鎖狀態(tài)(鎖標(biāo)志位為“01”狀態(tài)渊胸,是否為偏向鎖為“0”)旬盯,虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲鎖對象目前的Mark Word的拷貝翎猛,然后拷貝對象頭中的Mark Word復(fù)制到鎖記錄中胖翰。
    拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對象的Mark Word更新為指向Lock Record的指針切厘,并將Lock Record里的owner指針指向?qū)ο蟮腗ark Word萨咳。
    如果這個更新動作成功了,那么這個線程就擁有了該對象的鎖疫稿,并且對象Mark Word的鎖標(biāo)志位設(shè)置為“00”培他,表示此對象處于輕量級鎖定狀態(tài)。
    如果輕量級鎖的更新操作失敗了遗座,虛擬機(jī)首先會檢查對象的Mark Word是否指向當(dāng)前線程的棧幀舀凛,如果是就說明當(dāng)前線程已經(jīng)擁有了這個對象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行途蒋,否則說明多個線程競爭鎖猛遍。
    若當(dāng)前只有一個等待線程,則該線程通過自旋進(jìn)行等待号坡。但是當(dāng)自旋超過一定的次數(shù)螃壤,或者一個線程在持有鎖,一個在自旋筋帖,又有第三個來訪時奸晴,輕量級鎖升級為重量級鎖。
  • 重量級鎖
    升級為重量級鎖時日麸,鎖標(biāo)志的狀態(tài)值變?yōu)椤?0”寄啼,此時Mark Word中存儲的是指向重量級鎖的指針,此時等待鎖的線程都會進(jìn)入阻塞狀態(tài)代箭。
    整體的鎖狀態(tài)升級流程如下:
    無鎖 -> 偏向鎖 -> 輕量級鎖 -> 重量級鎖
    綜上墩划,偏向鎖通過對比Mark Word解決加鎖問題,避免執(zhí)行CAS操作嗡综。而輕量級鎖是通過用CAS操作和自旋來解決加鎖問題乙帮,避免線程阻塞和喚醒而影響性能。重量級鎖是將除了擁有鎖的線程以外的線程都阻塞极景。

文章參考:

https://www.cnblogs.com/jyroy/p/11365935.html#idx_0
https://www.studying.icu/pages/6649c3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末察净,一起剝皮案震驚了整個濱河市驾茴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌氢卡,老刑警劉巖锈至,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異译秦,居然都是意外死亡峡捡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門筑悴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來们拙,“玉大人,你說我怎么就攤上這事阁吝⊙馄牛” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵求摇,是天一觀的道長射沟。 經(jīng)常有香客問我殊者,道長与境,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任猖吴,我火速辦了婚禮摔刁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘海蔽。我一直安慰自己共屈,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布党窜。 她就那樣靜靜地躺著拗引,像睡著了一般。 火紅的嫁衣襯著肌膚如雪幌衣。 梳的紋絲不亂的頭發(fā)上矾削,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機(jī)與錄音豁护,去河邊找鬼哼凯。 笑死,一個胖子當(dāng)著我的面吹牛楚里,可吹牛的內(nèi)容都是我干的断部。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼班缎,長吁一口氣:“原來是場噩夢啊……” “哼蝴光!你這毒婦竟也來了她渴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤虱疏,失蹤者是張志新(化名)和其女友劉穎惹骂,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體做瞪,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡对粪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了装蓬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片著拭。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖牍帚,靈堂內(nèi)的尸體忽然破棺而出儡遮,到底是詐尸還是另有隱情,我是刑警寧澤暗赶,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布鄙币,位于F島的核電站,受9級特大地震影響蹂随,放射性物質(zhì)發(fā)生泄漏十嘿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一岳锁、第九天 我趴在偏房一處隱蔽的房頂上張望绩衷。 院中可真熱鬧,春花似錦激率、人聲如沸咳燕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽招盲。三九已至,卻和暖如春嘉冒,著一層夾襖步出監(jiān)牢的瞬間曹货,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工健爬, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留控乾,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓娜遵,卻偏偏與公主長得像蜕衡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子设拟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容