欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中synchronized的優(yōu)化措施

 更新時間:2023年05月17日 10:27:11   作者:真正的飛魚  
這篇文章主要介紹了Java中synchronized的優(yōu)化,介紹為了實現(xiàn)高效并發(fā),虛擬機(jī)對synchronized 做的一系列的鎖優(yōu)化措施,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下

本文介紹為了實現(xiàn)高效并發(fā),虛擬機(jī)對 synchronized 做的一系列的鎖優(yōu)化措施

高效并發(fā)是從 JDK5 升級到 JDK6 后一項重要的改進(jìn)項,HotSpot 虛擬機(jī)開發(fā)團(tuán)隊在 JDK6 這個版本上花費(fèi)了大量的資源去實現(xiàn)各種鎖優(yōu)化技術(shù),如適應(yīng)性自旋(Adaptive Spinning)、鎖消除(Lock Elimination)、鎖膨脹(Lock Coarsening)、 輕量級鎖(Lightweight Locking) 、偏向鎖(Biased Locking)等,這些技術(shù)都是為了在線程之間更高效地共享數(shù)據(jù)及解決競爭問題,從而提高程序的執(zhí)行效率。

自旋鎖 & 自適應(yīng)自旋

在許多應(yīng)用上,共享數(shù)據(jù)的鎖定狀態(tài)只會持續(xù)很短的一段時間,為了這段時間去掛起和恢復(fù)線程并不值得。

自旋鎖指的是:線程 A 成功獲取鎖后,線程 B 請求鎖時,請求鎖的線程 B 執(zhí)行一個忙循環(huán)(自旋),不放棄處理器的執(zhí)行時間,看看持有鎖的線程 A 是否會很快就釋放鎖。自旋等待的時間有一定的限度,如果自旋超過了限定的次數(shù)仍然沒有成功獲得鎖,就應(yīng)當(dāng)使用傳統(tǒng)的方式去掛起線程。

自適應(yīng)自旋指的是:自旋的時間不再是固定的了,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定的。

前面我們討論互斥同步的時候,提到了互斥同步對性能影響最大的是阻塞的實現(xiàn),掛起線程和恢復(fù)線程的操作都需要轉(zhuǎn)入內(nèi)核態(tài)中完成,這些操作給Java虛擬機(jī)的并發(fā)性能帶來了很大的壓力。同時,虛擬機(jī)的開發(fā)團(tuán)隊也注意到在許多應(yīng)用上,共享數(shù)據(jù)的鎖定狀態(tài)只會持續(xù)很短的一段時間,為了這段時間去掛起和恢復(fù)線程并不值得?,F(xiàn)在絕大多數(shù)的個人電腦和服務(wù)器都是多路(核)處理器系統(tǒng),如果物理機(jī)器有一個以上的處理器或者處理器核心,能讓兩個或以上的線程同時并行執(zhí)行,我們就可以讓后面請求鎖的那個線程“稍等一會”,但不放棄處理器的執(zhí)行時間,看看持有鎖的線程是否很快就會釋放鎖。為了讓線程等待,我們只須讓線程執(zhí)行一個忙循環(huán)(自旋),這項技術(shù)就是所謂的自旋鎖。

自旋鎖在 JDK1.4.2 中就已經(jīng)引入,只不過默認(rèn)是關(guān)閉的,可以使用 -XX:+UseSpinning 參數(shù)來開啟,在 JDK6 中就已經(jīng)改為默認(rèn)開啟了。自旋等待不能代替阻塞,且先不說對處理器數(shù)量的要求,自旋等待本身雖然避免了線程切換的開銷,但它是要占用處理器時間的,所以如果鎖被占用的時間很短,自旋等待的效果就會非常好,反之如果鎖被占用的時間很長, 那么自旋的線程只會白白消耗處理器資源,而不會做任何有價值的工作,這就會帶來性能的浪費(fèi)。因此自旋等待的時間必須有一定的限度,如果自旋超過了限定的次數(shù)仍然沒有成功獲得鎖,就應(yīng)當(dāng)使用傳統(tǒng)的方式去掛起線程。自旋次數(shù)的默認(rèn)值是十次,用戶也可以使用參數(shù) -XX:PreBlockSpin 來自行更改。

不過無論是默認(rèn)值還是用戶指定的自旋次數(shù),對整個 Java 虛擬機(jī)中所有的鎖來說都是相同的。在 JDK6 中對自旋鎖的優(yōu)化,引入了自適應(yīng)的自旋。自適應(yīng)意味著自旋的時間不再是固定的了,而是由前一次在同一個鎖上的自旋時間及鎖的擁有者的狀態(tài)來決定的。

  • 如果在同一個鎖對象上,自旋等待剛剛成功獲得過鎖, 并且持有鎖的線程正在運(yùn)行中,那么虛擬機(jī)就會認(rèn)為這次自旋也很有可能再次成功,進(jìn)而允許自旋等待持續(xù)相對更長的時間,比如持續(xù)100次忙循環(huán)。
  • 另一方面,如果對于某個鎖,自旋很少成功獲得過鎖,那在以后要獲取這個鎖時將有可能直接省略掉自旋過程,以避免浪費(fèi)處理器資源。

有了自適應(yīng)自旋,隨著程序運(yùn)行時間的增長及性能監(jiān)控信息的不斷完善,虛擬機(jī)對程序鎖的狀況預(yù)測就會越來越精準(zhǔn),虛擬機(jī)就會變得越來越“聰明”了。

鎖消除

鎖消除是指虛擬機(jī)即時編譯器在運(yùn)行時,對一些代碼要求同步,但是被檢測到不可能存在共享數(shù)據(jù)競爭的鎖進(jìn)行消除。鎖消除的主要判定依據(jù)來源于逃逸分析的數(shù)據(jù)支持,如果判斷到一段代碼中,在堆上的所有數(shù)據(jù)都不會逃逸出去被其他線程訪問到,那就可以把它們當(dāng)作棧上數(shù)據(jù)對待,認(rèn)為它們是線程私有的,同步加鎖自然就無須再進(jìn)行。

也許讀者會有疑問,變量是否逃逸,對于虛擬機(jī)來說是需要使用復(fù)雜的過程間分析才能確定的,但是程序員自己應(yīng)該是很清楚的,怎么會在明知道不存在數(shù)據(jù)爭用的情況下還要求同步呢?這個問題的答案是:有許多同步措施并不是程序員自己加入的,同步的代碼在 Java 程序中出現(xiàn)的頻繁程度也許超過了大部分讀者的想象。我們來看看如代碼清單13-6所示的例子,這段非常簡單的代碼僅僅是輸出三個字符串相加的結(jié)果,無論是源代碼字面上, 還是程序語義上都沒有進(jìn)行同步。

// 代碼清單13-6 一段看起來沒有同步的代碼
public String concatString(String s1, String s2, String s3) {
    return s1 + s2 + s3;
}

我們也知道,由于 String 是一個不可變的類,對字符串的連接操作總是通過生成新的 String 對象來進(jìn)行的,因此 Javac 編譯器會對 String 連接做自動優(yōu)化。

  • 在 JDK5 之前,字符串加法會轉(zhuǎn)化為 StringBuffer 對象的連續(xù) append() 操作。即代碼清單13-6所示的代碼可能會變成代碼清單13-7所示的樣子。
  • 在 JDK5 及以后的版本中,會轉(zhuǎn)化為 StringBuilder 對象的連續(xù) append() 操作。
// 代碼清單13-7 Javac轉(zhuǎn)化后的字符串連接操作
public String concatString(String s1, String s2, String s3) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    sb.append(s3);
    return sb.toString();
}

現(xiàn)在大家還認(rèn)為這段代碼沒有涉及同步嗎?每個 StringBuffer.append() 方法中都有一個同步塊,鎖就是 sb 對象。虛擬機(jī)觀察 sb 變量,經(jīng)過逃逸分析后會發(fā)現(xiàn)它的動態(tài)作用域被限制在 concatString() 方法內(nèi)部。也就是 sb 的所有引用都永遠(yuǎn)不會逃逸到 concatString() 方法之外,其他線程無法訪問到它,所以這里雖然有鎖,但是可以被安全地消除掉。在解釋執(zhí)行時這里仍然會加鎖,但在經(jīng)過服務(wù)端編譯器的即時編譯之后,這段代碼就會忽略所有的同步措施而直接執(zhí)行。

鎖粗化

鎖粗化指的是:如果虛擬機(jī)探測到有一串零碎的操作都對同一個對象加鎖,那么虛擬機(jī)將會把加鎖同步的范圍擴(kuò)展(粗化)到整個操作序列的外部。

原則上,我們在編寫代碼的時候,總是推薦將同步塊的作用范圍限制得盡量?。褐辉诠蚕頂?shù)據(jù)的實際作用域中才進(jìn)行同步,這樣是為了使得需要同步的操作數(shù)量盡可能變少,即使存在鎖競爭,等待鎖的線程也能盡可能快地拿到鎖。

大多數(shù)情況下,上面的原則都是正確的,但是如果一系列的連續(xù)操作都對同一個對象反復(fù)加鎖和解鎖,甚至加鎖操作是出現(xiàn)在循環(huán)體之中的,那即使沒有線程競爭,頻繁地進(jìn)行互斥同步操作也會導(dǎo)致不必要的性能損耗。

代碼清單13-7所示連續(xù)的 append() 方法就屬于這類情況。如果虛擬機(jī)探測到有這樣一串零碎的操作都對同一個對象加鎖,將會把加鎖同步的范圍擴(kuò)展(粗化)到整個操作序列的外部,以代碼清單13-7為例,就是擴(kuò)展到第一個 append() 操作之前直至最后一個 append() 操作之后,這樣只需要加鎖一次就可以了。

輕量級鎖

輕量級鎖的設(shè)計初衷是在沒有多線程競爭的情況下,通過使用 CAS(Compare And Swap)操作來進(jìn)行線程同步,減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。

輕量級鎖可以提高帶有同步但無競爭的程序性能,但它是一個帶有效益權(quán)衡(Trade Off) 性質(zhì)的優(yōu)化,也就是說它并非總是對程序運(yùn)行有利。輕量級鎖能提升程序同步性能的依據(jù)是 “對于絕大部分的鎖,在整個同步周期內(nèi)都是不存在競爭的” 這一經(jīng)驗法則。

  • 如果沒有競爭,輕量級鎖便通過 CAS 操作成功避免了使用互斥量的開銷;
  • 但如果確實存在鎖競爭,除了互斥量的本身開銷外,還額外發(fā)生了 CAS 操作的開銷。

因此在有競爭的情況下,輕量級鎖反而會比傳統(tǒng)的重量級鎖更慢。

輕量級鎖是 JDK6 時加入的新型鎖機(jī)制,它名字中的 “輕量級” 是相對于使用操作系統(tǒng)互斥量來實現(xiàn)的傳統(tǒng)鎖而言的, 因此傳統(tǒng)的鎖機(jī)制就被稱為“重量級”鎖。不過,需要強(qiáng)調(diào)一點(diǎn),輕量級鎖并不是用來代替重量級鎖的,輕量級鎖設(shè)計的初衷是在沒有多線程競爭的前提下,減少傳統(tǒng)的重量級鎖使用操作系統(tǒng)互斥量產(chǎn)生的性能消耗。

Mark Word

要理解輕量級鎖,以及后面會講到的偏向鎖的原理和運(yùn)作過程,必須要對 HotSpot 虛擬機(jī)對象的內(nèi)存布局(尤其是對象頭部分)有所了解。HotSpot 虛擬機(jī)的對象頭(Object Header)分為兩部分:

  • 第一部分用于存儲對象自身的運(yùn)行時數(shù)據(jù),如哈希碼(HashCode)、GC 分代年齡(Generational GC Age)等。這部分?jǐn)?shù)據(jù)的長度在 32 位和 64 位的 Java 虛擬機(jī)中分別會占用 32 個或 64 個比特,官方稱它為 “Mark Word”。這部分是實現(xiàn)輕量級鎖和偏向鎖的關(guān)鍵。
  • 另外一部分用于存儲指向方法區(qū)對象類型數(shù)據(jù)的指針(Class Pointer、類型指針),虛擬機(jī)通過這個指針來確定這個對象是哪個類的實例。如果是數(shù)組對象,還會有一個額外的部分用于存儲數(shù)組長度。

由于對象頭信息是與對象自身定義的數(shù)據(jù)無關(guān)的額外存儲成本,考慮到 Java 虛擬機(jī)的空間使用效率,Mark Word 被設(shè)計成一個非固定的動態(tài)數(shù)據(jù)結(jié)構(gòu),以便在極小的空間內(nèi)存儲盡量多的信息。它會根據(jù)對象的狀態(tài)復(fù)用自己的存儲空間。例如在 32 位的 HotSpot 虛擬機(jī)中:

  • 對象未被鎖定的狀態(tài)下,Mark Word 的 32 個比特空間里的 25 個比特將用于存儲對象哈希碼,4 個比特用于存儲對象分代年齡,2 個比特用于存儲鎖標(biāo)志位,還有 1 個比特固定為 0(這表示未進(jìn)入偏向模式)。
  • 對象除了未被鎖定的正常狀態(tài)外,還有輕量級鎖定、重量級鎖定、GC 標(biāo)記、可偏向等幾種不同狀態(tài),這些狀態(tài)下對象頭的存儲內(nèi)容如下表所示。

工作過程

我們簡單回顧了對象的內(nèi)存布局后,接下來就可以介紹輕量級鎖的工作過程了:在代碼即將進(jìn)入同步塊的時候,如果此同步對象沒有被鎖定(鎖標(biāo)志位為“01”狀態(tài)),虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間, 用于存儲鎖對象目前的 Mark Word 的拷貝(官方為這份拷貝加了一個 Displaced 前綴,即 Displaced Mark Word),這時候線程堆棧與對象頭的狀態(tài)如圖13-3所示。

圖13-3輕量級鎖 CAS 操作之前堆棧與對象的狀態(tài)

然后, 虛擬機(jī)將使用 CAS 操作嘗試把對象的 Mark Word 更新為指向鎖記錄(Lock Record)的指針。

  • 如果這個更新操作成功了,即代表該線程擁有了這個對象的鎖,并且對象 Mark Word 的鎖標(biāo)志位(Mark Word 的最后兩個比特)將轉(zhuǎn)變?yōu)?“00”,表示此對象處于輕量級鎖定狀態(tài)。這時候線程堆棧與對象頭的狀態(tài)如圖13-4所示。
  • 如果這個更新操作失敗了,那就意味著至少存在一條線程與當(dāng)前線程競爭獲取該對象的鎖。虛擬機(jī)首先會檢查對象的 Mark Word 是否指向當(dāng)前線程的棧幀,如果是,說明當(dāng)前線程已經(jīng)擁有了這個對象的鎖,那直接進(jìn)入同步塊繼續(xù)執(zhí)行就可以了,否則(對象的 Mark Word 不是指向當(dāng)前線程的棧幀)就說明這個鎖對象已經(jīng)被其他線程搶占了。如果出現(xiàn)兩條以上的線程爭用同一個鎖的情況,那輕量級鎖就不再有效,必須要膨脹為重量級鎖,鎖標(biāo)志的狀態(tài)值變?yōu)?ldquo;10”,此時 Mark Word 中存儲的就是指向重量級鎖(互斥量)的指針,后面等待鎖的線程也必須進(jìn)入阻塞狀態(tài)。

圖13-4輕量級鎖 CAS 操作之后堆棧與對象的狀態(tài)

上面描述的是輕量級鎖的加鎖過程,它的解鎖過程也同樣是通過 CAS 操作來進(jìn)行的,如果對象的 Mark Word 仍然指向線程的鎖記錄,那就用 CAS 操作把對象當(dāng)前的 Mark Word 和線程中復(fù)制的 Displaced Mark Word 替換回來。

  • 假如能夠替換成功,那整個同步過程就順利完成了;
  • 如果替換失敗,則說明有其他線程嘗試過獲取該鎖,就要在釋放鎖的同時,喚醒被掛起的線程。

輕量級鎖能提升程序同步性能的依據(jù)是 “對于絕大部分的鎖,在整個同步周期內(nèi)都是不存在競爭的” 這一經(jīng)驗法則。

  • 如果沒有競爭,輕量級鎖便通過 CAS 操作成功避免了使用互斥量的開銷;
  • 但如果確實存在鎖競爭,除了互斥量的本身開銷外,還額外發(fā)生了 CAS 操作的開銷。

因此在有競爭的情況下,輕量級鎖反而會比傳統(tǒng)的重量級鎖更慢。

偏向鎖

偏向鎖的目的是:消除數(shù)據(jù)在無競爭情況下的同步原語,進(jìn)一步提高程序的運(yùn)行性能。

偏向鎖中的“偏”的意思是這個鎖會偏向于第一個獲得它的線程。如果虛擬機(jī)啟用了偏向鎖,那么當(dāng)鎖對象第一次被線程獲取的時候,虛擬機(jī)將會把對象頭中的標(biāo)志位設(shè)置為 “01”、把偏向模式設(shè)置為 “1”,表示進(jìn)入偏向模式。同時使用 CAS 操作把獲取到這個鎖的線程的 ID 記錄在對象的 Mark Word 之中。如果 CAS 操作成功,持有偏向鎖的線程以后每次進(jìn)入這個鎖相關(guān)的同步塊時,虛擬機(jī)都可以不再進(jìn)行任何同步操作(例如加鎖、解鎖及對 Mark Word 的更新操作等)。

偏向鎖可以提高帶有同步但無競爭的程序性能,但它同樣是一個帶有效益權(quán)衡(Trade Off) 性質(zhì)的優(yōu)化,也就是說它并非總是對程序運(yùn)行有利。如果程序中大多數(shù)的鎖都總是被多個不同的線程訪問,那偏向模式就是多余的。

偏向鎖也是 JDK6 中引入的一項鎖優(yōu)化措施,它的目的是消除數(shù)據(jù)在無競爭情況下的同步原語,進(jìn)一步提高程序的運(yùn)行性能。如果說輕量級鎖是在無競爭的情況下使用 CAS 操作去消除同步使用的互斥量,那偏向鎖就是在無競爭的情況下把整個同步都消除掉,連 CAS 操作都不去做了。

偏向鎖中的“偏”,就是偏心的“偏”、偏袒的“偏”。偏向鎖中的“偏”的意思是這個鎖會偏向于第一個獲得它的線程,如果在接下來的執(zhí)行過程中,該鎖一直沒有被其他的線程獲取,則持有偏向鎖的線程將永遠(yuǎn)不需要再進(jìn)行同步。

如果讀者理解了前面輕量級鎖中關(guān)于對象頭 Mark Word 與線程之間的操作過程,那偏向鎖的原理就會很容易理解。

假設(shè)當(dāng)前虛擬機(jī)啟用了偏向鎖(啟用參數(shù) -XX:+UseBiased Locking,這是自 JDK6 起 HotSpot 虛擬機(jī)的默認(rèn)值),那么當(dāng)鎖對象第一次被線程獲取的時候,虛擬機(jī)將會把對象頭中的標(biāo)志位設(shè)置為 “01”、把偏向模式設(shè)置為 “1”,表示進(jìn)入偏向模式。同時使用 CAS 操作把獲取到這個鎖的線程的 ID 記錄在對象的 Mark Word 之中。如果 CAS 操作成功,持有偏向鎖的線程以后每次進(jìn)入這個鎖相關(guān)的同步塊時,虛擬機(jī)都可以不再進(jìn)行任何同步操作(例如加鎖、解鎖及對 Mark Word 的更新操作等)。

一旦出現(xiàn)另外一個線程去嘗試獲取這個鎖的情況,偏向模式就馬上宣告結(jié)束。根據(jù)鎖對象目前是否處于被鎖定的狀態(tài)決定是否撤銷偏向(偏向模式設(shè)置為 “0”),撤銷后標(biāo)志位恢復(fù)到未鎖定(標(biāo)志位為 “01”)或輕量級鎖定(標(biāo)志位為 “00”)的狀態(tài),后續(xù)的同步操作就按照上面介紹的輕量級鎖那樣去執(zhí)行。

偏向鎖、輕量級鎖的狀態(tài)轉(zhuǎn)換及對象 Mark Word 的關(guān)系如圖13-5所示。

圖13-5偏向鎖、輕量級鎖的狀態(tài)轉(zhuǎn)換及及對象 Mark Word 的關(guān)系

細(xì)心的讀者看到這里可能會發(fā)現(xiàn)一個問題:當(dāng)對象進(jìn)入偏向狀態(tài)的時候,Mark Word 大部分的空間(23個比特) 都用于存儲持有鎖的線程 ID 了,這部分空間占用了原有存儲對象哈希碼的位置,那原來對象的哈希碼怎么辦呢?

在 Java 語言里面一個對象如果計算過哈希碼,就應(yīng)該一直保持該值不變(強(qiáng)烈推薦但不強(qiáng)制,因為用戶可以重載hashCode() 方法按自己的意愿返回哈希碼),否則很多依賴對象哈希碼的 API 都可能存在出錯風(fēng)險。而作為絕大多數(shù)對象哈希碼來源的 Object::hashCode() 方法,返回的是對象的一致性哈希碼(Identity Hash Code),這個值是能強(qiáng)制保證不變的,它通過在對象頭中存儲計算結(jié)果來保證第一次計算之后,再次調(diào)用該方法取到的哈希碼值永遠(yuǎn)不會再發(fā)生改變。 因此,當(dāng)一個對象已經(jīng)計算過一致性哈希碼后,它就再也無法進(jìn)入偏向鎖狀態(tài)了;而當(dāng)一個對象當(dāng)前正處于偏向鎖狀態(tài), 又收到需要計算其一致性哈希碼請求時,它的偏向狀態(tài)會被立即撤銷,并且鎖會膨脹為重量級鎖。在重量級鎖的實現(xiàn)中, 對象頭指向了重量級鎖的位置,代表重量級鎖的 ObjectMonitor 類里有字段可以記錄非加鎖狀態(tài)(標(biāo)志位為“01”)下的Mark Word,其中自然可以存儲原來的哈希碼。

注意, 這里說的計算請求應(yīng)來自于對Object::hashCode()或者System::identityHashCode(Object)方法的調(diào)用, 如果重寫了對象的hashCode()方法, 計算哈希碼時并不會產(chǎn)生這里所說的請求。

偏向鎖可以提高帶有同步但無競爭的程序性能,但它同樣是一個帶有效益權(quán)衡(Trade Off) 性質(zhì)的優(yōu)化,也就是說它并非總是對程序運(yùn)行有利。如果程序中大多數(shù)的鎖都總是被多個不同的線程訪問,那偏向模式就是多余的。在具體問題具體分析的前提下,有時候使用參數(shù)-XX:-UseBiasedLocking 來禁止偏向鎖優(yōu)化反而可以提升性能。

完整的過程

假設(shè)當(dāng)前虛擬機(jī)啟用了偏向鎖,那么當(dāng)鎖對象第一次被線程獲取的時候,虛擬機(jī)將會把對象頭中的標(biāo)志位設(shè)置為 “01”、把偏向模式設(shè)置為 “1”,表示進(jìn)入偏向模式。同時使用 CAS 操作把獲取到這個鎖的線程的 ID 記錄在對象的 Mark Word 之中。如果 CAS 操作成功,持有偏向鎖的線程以后每次進(jìn)入這個鎖相關(guān)的同步塊時,虛擬機(jī)都可以不再進(jìn)行任何同步操作(例如加鎖、解鎖及對 Mark Word 的更新操作等)。

如果鎖對象目前處于偏向模式,那么一旦出現(xiàn)另外一個線程去嘗試獲取這個鎖的情況,偏向模式就馬上宣告結(jié)束。根據(jù)鎖對象目前是否處于被鎖定的狀態(tài)決定撤銷偏向后,鎖對象處于什么狀態(tài)。

  • 如果鎖對象目前處于被鎖定的狀態(tài),那么一旦出現(xiàn)另外一個線程去嘗試獲取這個鎖的情況,偏向模式就馬上宣告結(jié)束,鎖對象轉(zhuǎn)換到輕量級鎖定狀態(tài),后續(xù)的同步操作就按照輕量級鎖那樣去執(zhí)行。
  • 如果鎖對象目前處于未被鎖定的狀態(tài),那么一旦出現(xiàn)另外一個線程去嘗試獲取這個鎖的情況,偏向模式就馬上宣告結(jié)束,鎖對象轉(zhuǎn)換到未被鎖定、不可偏向狀態(tài)。

對象轉(zhuǎn)換到輕量級鎖定狀態(tài)。虛擬機(jī)首先將在當(dāng)前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲鎖對象目前的 Mark Word 的拷貝。然后,虛擬機(jī)將使用 CAS 操作嘗試把對象的 Mark Word 更新為指向鎖記錄(Lock Record)的指針。

  • 如果這個更新操作成功了,即代表該線程擁有了這個對象的鎖,并且對象 Mark Word 的鎖標(biāo)志位將轉(zhuǎn)變?yōu)?“00”,表示此對象處于輕量級鎖定狀態(tài)。
  • 如果這個更新操作失敗了,那就意味著至少存在一條線程與當(dāng)前線程競爭獲取該對象的鎖。虛擬機(jī)首先會檢查對象的 Mark Word 是否指向當(dāng)前線程的棧幀:
    • 如果是(對象的 Mark Word 指向當(dāng)前線程的棧幀),說明當(dāng)前線程已經(jīng)擁有了這個對象的鎖,那直接進(jìn)入同步塊繼續(xù)執(zhí)行就可以了;
    • 否則(對象的 Mark Word 不是指向當(dāng)前線程的棧幀)就說明這個鎖對象已經(jīng)被其他線程搶占了,那么當(dāng)前線程 B 執(zhí)行一個忙循環(huán)(自旋),不放棄處理器的執(zhí)行時間,看看持有鎖的線程 A 是否會很快就釋放鎖。
      • 如果持有鎖的線程 A 很快就釋放了鎖,那么當(dāng)前線程 B 成功獲取鎖。
      • 如果線程 B 自旋超過了限定的次數(shù)仍然沒有成功獲得鎖,那輕量級鎖就不再有效,必須要膨脹為重量級鎖,鎖標(biāo)志的狀態(tài)值變?yōu)?ldquo;10”,此時 Mark Word 中存儲的就是指向重量級鎖(互斥量)的指針。當(dāng)前線程繼續(xù)等待鎖,并進(jìn)入阻塞狀態(tài)。持有鎖的線程 A 釋放鎖的同時,喚醒被掛起的線程。被喚醒的線程就會進(jìn)行新一輪的競爭,嘗試獲取這個鎖。

參考資料

第13章 線程安全與鎖優(yōu)化 13.3 鎖優(yōu)化

到此這篇關(guān)于Java中synchronized的優(yōu)化 的文章就介紹到這了,更多相關(guān)Java synchronized優(yōu)化 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 實現(xiàn)麥克風(fēng)自動錄音

    Java 實現(xiàn)麥克風(fēng)自動錄音

    這篇文章主要介紹了Java 實現(xiàn)麥克風(fēng)自動錄音的示例代碼,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-12-12
  • SpringBoot內(nèi)部外部配置文件加載順序解析

    SpringBoot內(nèi)部外部配置文件加載順序解析

    這篇文章主要介紹了SpringBoot內(nèi)部外部配置文件加載順序解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-07-07
  • mybatis不加@Parm注解報錯的解決方案

    mybatis不加@Parm注解報錯的解決方案

    這篇文章主要介紹了mybatis不加@Parm注解報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • IDEA Spring Boot 自動化構(gòu)建+部署的實現(xiàn)

    IDEA Spring Boot 自動化構(gòu)建+部署的實現(xiàn)

    這篇文章主要介紹了IDEA Spring Boot 自動化構(gòu)建+部署的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • C/C++和Java的交互詳解

    C/C++和Java的交互詳解

    本文旨在簡單的介紹一下如何讓java層和C/C++層相互調(diào)用,這里主要是使用了JNI技術(shù),并沒有深究其原理,只是做了個實現(xiàn),其目的是為后面的學(xué)習(xí)打開一扇窗。
    2016-01-01
  • Java?File類提供的方法與操作

    Java?File類提供的方法與操作

    Java使用File類來表示計算機(jī)系統(tǒng)磁盤文件的對象類型。File中提供了大量的方法,可以對文件進(jìn)行增加、刪除、修改、重命名等常規(guī)操作。File類的對象會存儲文件自身的信息,例如文件在系統(tǒng)中的存儲目錄、文件大小、文件讀寫權(quán)限等
    2023-03-03
  • Spring Boot基礎(chǔ)學(xué)習(xí)之Mybatis操作中使用Redis做緩存詳解

    Spring Boot基礎(chǔ)學(xué)習(xí)之Mybatis操作中使用Redis做緩存詳解

    這篇文章主要給大家介紹了關(guān)于Spring Boot基礎(chǔ)學(xué)習(xí)之Mybatis操作中使用Redis做緩存的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧
    2018-11-11
  • 詳解Java接口簽名(Signature)實現(xiàn)方案

    詳解Java接口簽名(Signature)實現(xiàn)方案

    這篇文章主要介紹了Java接口簽名(Signature)實現(xiàn)方案?,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-01-01
  • Java中繼承thread類與實現(xiàn)Runnable接口的比較

    Java中繼承thread類與實現(xiàn)Runnable接口的比較

    這篇文章主要介紹了Java中繼承thread類與實現(xiàn)Runnable接口的比較的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • Spring Boot Rest控制器單元測試過程解析

    Spring Boot Rest控制器單元測試過程解析

    這篇文章主要介紹了Spring Boot Rest控制器單元測試過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-03-03

最新評論