轻量级锁_并发编程实战05:锁的状态
無鎖、偏向鎖 、輕量級(jí)鎖和重量級(jí)鎖這四種鎖是指鎖的狀態(tài),專門針對(duì)synchronized的。在介紹這四種鎖狀態(tài)之前還需要介紹一些額外的知識(shí)。
首先為什么Synchronized能實(shí)現(xiàn)線程同步?在回答這個(gè)問題之前我們需要了解兩個(gè)重要的概念:“Java對(duì)象頭”、“Monitor”。
Java對(duì)象頭
synchronized是悲觀鎖,在操作同步資源之前需要給同步資源先加鎖,這把鎖就是存在Java對(duì)象頭里的,而Java對(duì)象頭又是什么呢?
我們以Hotspot虛擬機(jī)為例,Hotspot的對(duì)象頭主要包括兩部分?jǐn)?shù)據(jù):Mark Word(標(biāo)記字段)、Klass Pointer(類型指針)。
Mark Word:默認(rèn)存儲(chǔ)對(duì)象的HashCode,分代年齡和鎖標(biāo)志位信息。這些信息都是與對(duì)象自身定義無關(guān)的數(shù)據(jù),所以Mark Word被設(shè)計(jì)成一個(gè)非固定的數(shù)據(jù)結(jié)構(gòu)以便在極小的空間內(nèi)存存儲(chǔ)盡量多的數(shù)據(jù)。它會(huì)根據(jù)對(duì)象的狀態(tài)復(fù)用自己的存儲(chǔ)空間,也就是說在運(yùn)行期間Mark Word里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化。
Klass Point:對(duì)象指向它的類元數(shù)據(jù)的指針,虛擬機(jī)通過這個(gè)指針來確定這個(gè)對(duì)象是哪個(gè)類的實(shí)例。
Monitor
Monitor可以理解為一個(gè)同步工具或一種同步機(jī)制,通常被描述為一個(gè)對(duì)象。每一個(gè)Java對(duì)象就有一把看不見的鎖,稱為內(nèi)部鎖或者M(jìn)onitor鎖。
Monitor是線程私有的數(shù)據(jù)結(jié)構(gòu),每一個(gè)線程都有一個(gè)可用monitor record列表,同時(shí)還有一個(gè)全局的可用列表。每一個(gè)被鎖住的對(duì)象都會(huì)和一個(gè)monitor關(guān)聯(lián),同時(shí)monitor中有一個(gè)Owner字段存放擁有該鎖的線程的唯一標(biāo)識(shí),表示該鎖被這個(gè)線程占用。
現(xiàn)在話題回到synchronized,synchronized通過Monitor來實(shí)現(xiàn)線程同步,Monitor是依賴于底層的操作系統(tǒng)的Mutex Lock(互斥鎖)來實(shí)現(xiàn)的線程同步。
如同我們?cè)谧孕i中提到的“阻塞或喚醒一個(gè)Java線程需要操作系統(tǒng)切換CPU狀態(tài)來完成,這種狀態(tài)轉(zhuǎn)換需要耗費(fèi)處理器時(shí)間。如果同步代碼塊中的內(nèi)容過于簡(jiǎn)單,狀態(tài)轉(zhuǎn)換消耗的時(shí)間有可能比用戶代碼執(zhí)行的時(shí)間還要長(zhǎng)”。這種方式就是synchronized最初實(shí)現(xiàn)同步的方式,這就是JDK 6之前synchronized效率低的原因。這種依賴于操作系統(tǒng)Mutex Lock所實(shí)現(xiàn)的鎖我們稱之為“重量級(jí)鎖”,JDK 6中為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了“偏向鎖”和“輕量級(jí)鎖”。
所以目前鎖一共有4種狀態(tài),級(jí)別從低到高依次是:無鎖、偏向鎖、輕量級(jí)鎖和重量級(jí)鎖。鎖狀態(tài)只能升級(jí)不能降級(jí)。
通過上面的介紹,我們對(duì)synchronized的加鎖機(jī)制以及相關(guān)知識(shí)有了一個(gè)了解,那么下面我們給出四種鎖狀態(tài)對(duì)應(yīng)的的Mark Word內(nèi)容,然后再分別講解四種鎖狀態(tài)的思路以及特點(diǎn):
| 無鎖 | 對(duì)象的hashCode、對(duì)象分代年齡、是否是偏向鎖(0) | 01 |
| 偏向鎖 | 偏向線程ID、偏向時(shí)間戳、對(duì)象分代年齡、是否是偏向鎖(1) | 01 |
| 輕量級(jí)鎖 | 指向棧中鎖記錄的指針 | 00 |
| 重量級(jí)鎖 | 指向互斥量(重量級(jí)鎖)的指針 | 10 |
無鎖
無鎖沒有對(duì)資源進(jìn)行鎖定,所有的線程都能訪問并修改同一個(gè)資源,但同時(shí)只有一個(gè)線程能修改成功。
無鎖的特點(diǎn)就是修改操作在循環(huán)內(nèi)進(jìn)行,線程會(huì)不斷的嘗試修改共享資源。如果沒有沖突就修改成功并退出,否則就會(huì)繼續(xù)循環(huán)嘗試。如果有多個(gè)線程修改同一個(gè)值,必定會(huì)有一個(gè)線程能修改成功,而其他修改失敗的線程會(huì)不斷重試直到修改成功。上面我們介紹的CAS原理及應(yīng)用即是無鎖的實(shí)現(xiàn)。無鎖無法全面代替有鎖,但無鎖在某些場(chǎng)合下的性能是非常高的。
偏向鎖
偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問,那么該線程會(huì)自動(dòng)獲取鎖,降低獲取鎖的代價(jià)。
在大多數(shù)情況下,鎖總是由同一線程多次獲得,不存在多線程競(jìng)爭(zhēng),所以出現(xiàn)了偏向鎖。其目標(biāo)就是在只有一個(gè)線程執(zhí)行同步代碼塊時(shí)能夠提高性能。
當(dāng)一個(gè)線程訪問同步代碼塊并獲取鎖時(shí),會(huì)在Mark Word里存儲(chǔ)鎖偏向的線程ID。在線程進(jìn)入和退出同步塊時(shí)不再通過CAS操作來加鎖和解鎖,而是檢測(cè)Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。引入偏向鎖是為了在無多線程競(jìng)爭(zhēng)的情況下盡量減少不必要的輕量級(jí)鎖執(zhí)行路徑,因?yàn)檩p量級(jí)鎖的獲取及釋放依賴多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時(shí)候依賴一次CAS原子指令即可。
偏向鎖只有遇到其他線程嘗試競(jìng)爭(zhēng)偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放鎖,線程不會(huì)主動(dòng)釋放偏向鎖。偏向鎖的撤銷,需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒有字節(jié)碼正在執(zhí)行),它會(huì)首先暫停擁有偏向鎖的線程,判斷鎖對(duì)象是否處于被鎖定狀態(tài)。撤銷偏向鎖后恢復(fù)到無鎖(標(biāo)志位為“01”)或輕量級(jí)鎖(標(biāo)志位為“00”)的狀態(tài)。
偏向鎖在JDK 6及以后的JVM里是默認(rèn)啟用的。可以通過JVM參數(shù)關(guān)閉偏向鎖:-XX:-UseBiasedLocking=false,關(guān)閉之后程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)。
輕量級(jí)鎖
是指當(dāng)鎖是偏向鎖的時(shí)候,被另外的線程所訪問,偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過自旋的形式嘗試獲取鎖,不會(huì)阻塞,從而提高性能。
在代碼進(jìn)入同步塊的時(shí)候,如果同步對(duì)象鎖狀態(tài)為無鎖狀態(tài)(鎖標(biāo)志位為“01”狀態(tài),是否為偏向鎖為“0”),虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個(gè)名為鎖記錄(Lock Record)的空間,用于存儲(chǔ)鎖對(duì)象目前的Mark Word的拷貝,然后拷貝對(duì)象頭中的Mark Word復(fù)制到鎖記錄中。
拷貝成功后,虛擬機(jī)將使用CAS操作嘗試將對(duì)象的Mark Word更新為指向Lock Record的指針,并將Lock Record里的owner指針指向?qū)ο蟮腗ark Word。
如果這個(gè)更新動(dòng)作成功了,那么這個(gè)線程就擁有了該對(duì)象的鎖,并且對(duì)象Mark Word的鎖標(biāo)志位設(shè)置為“00”,表示此對(duì)象處于輕量級(jí)鎖定狀態(tài)。
如果輕量級(jí)鎖的更新操作失敗了,虛擬機(jī)首先會(huì)檢查對(duì)象的Mark Word是否指向當(dāng)前線程的棧幀,如果是就說明當(dāng)前線程已經(jīng)擁有了這個(gè)對(duì)象的鎖,那就可以直接進(jìn)入同步塊繼續(xù)執(zhí)行,否則說明多個(gè)線程競(jìng)爭(zhēng)鎖。
若當(dāng)前只有一個(gè)等待線程,則該線程通過自旋進(jìn)行等待。但是當(dāng)自旋超過一定的次數(shù),或者一個(gè)線程在持有鎖,一個(gè)在自旋,又有第三個(gè)來訪時(shí),輕量級(jí)鎖升級(jí)為重量級(jí)鎖。
重量級(jí)鎖
升級(jí)為重量級(jí)鎖時(shí),鎖標(biāo)志的狀態(tài)值變?yōu)椤?0”,此時(shí)Mark Word中存儲(chǔ)的是指向重量級(jí)鎖的指針,此時(shí)等待鎖的線程都會(huì)進(jìn)入阻塞狀態(tài)。
小結(jié)
綜上,偏向鎖通過對(duì)比Mark Word解決加鎖問題,避免執(zhí)行CAS操作。而輕量級(jí)鎖是通過用CAS操作和自旋來解決加鎖問題,避免線程阻塞和喚醒而影響性能。重量級(jí)鎖是將除了擁有鎖的線程以外的線程都阻塞。
總結(jié)
以上是生活随笔為你收集整理的轻量级锁_并发编程实战05:锁的状态的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转】C++标准转换运算符static_
- 下一篇: TongWeb基本使用