您好,登錄后才能下訂單哦!
這篇文章主要講解了“java中鎖的優(yōu)化方法”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“java中鎖的優(yōu)化方法”吧!
1)鎖消除
概念:JVM在JIT編譯(即時(shí)編譯)時(shí),通過(guò)對(duì)運(yùn)行上下文的掃描,去除掉那些不可能發(fā)生共享資源競(jìng)爭(zhēng)的鎖,從而節(jié)省了線程請(qǐng)求這些鎖的時(shí)間。 舉例: StringBuffer的append方法是一個(gè)同步方法,如果StringBuffer類(lèi)型的變量是一個(gè)局部變量,則該變量就不會(huì)被其它線程所使用,即對(duì)局部變量的操作是不會(huì)發(fā)生線程不安全的問(wèn)題。 在這種情景下,JVM會(huì)在JIT編譯時(shí)自動(dòng)將append方法上的鎖去掉。
2)鎖粗化
概念:將多個(gè)連續(xù)的加鎖、解鎖操作連接在一起,擴(kuò)展成一個(gè)范圍更大的鎖,即將加鎖的粒度放大。 舉例:在for循環(huán)里的加鎖/解鎖操作,一般需要放到for循環(huán)外。
3)使用偏向鎖和輕量級(jí)鎖
說(shuō)明: 1)java6為了減少獲取鎖和釋放鎖帶來(lái)的性能消耗,引入了偏向鎖和輕量級(jí)鎖。 2)鎖一共有4種狀態(tài),級(jí)別從低到高依次是:無(wú)鎖狀態(tài)、偏向鎖、輕量級(jí)鎖、重量級(jí)鎖。 3)鎖的狀態(tài)會(huì)隨著競(jìng)爭(zhēng)情況逐漸升級(jí),并且只可以升級(jí)而不能降級(jí)。 【偏向鎖】 1)背景:大多數(shù)情況下,鎖不僅不存在多線程競(jìng)爭(zhēng),而且總是由同一線程多次獲得,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖。 2)概念:核心思想就是鎖會(huì)偏向第一個(gè)獲取它的線程,如果在接下來(lái)的執(zhí)行過(guò)程中沒(méi)有其它的線程獲取該鎖,則持有偏向鎖的線程永遠(yuǎn)不需要同步。 3)目的:偏向鎖實(shí)際上是一種優(yōu)化鎖,其目的是為了減少數(shù)據(jù)在無(wú)競(jìng)爭(zhēng)情況下的性能損耗。 4)原理: 1>當(dāng)一個(gè)線程訪問(wèn)同步塊并獲取鎖時(shí),會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)鎖偏向的線程ID。 2>以后該線程在進(jìn)入和退出同步塊時(shí)就不需要進(jìn)行CAS操作來(lái)加鎖和解鎖,只需簡(jiǎn)單地判斷一下對(duì)象頭的Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖。 5)偏向鎖的獲?。? 1>訪問(wèn)Mark Word中偏向鎖的標(biāo)識(shí)位是否為1,如果是1,則確定為偏向鎖。 說(shuō)明: [1]如果偏向鎖的標(biāo)識(shí)位為0,說(shuō)明此時(shí)是處于無(wú)鎖狀態(tài),則當(dāng)前線程通過(guò)CAS操作嘗試獲取偏向鎖,如果獲取鎖成功,則將Mark Word中的偏向線程ID設(shè)置為當(dāng)前線程ID;并且將偏向標(biāo)識(shí)位設(shè)為1。 [2]如果偏向鎖的標(biāo)識(shí)位不為1,也不為0(此時(shí)偏向鎖的標(biāo)識(shí)位沒(méi)有值),說(shuō)明發(fā)生了競(jìng)爭(zhēng),偏向鎖已經(jīng)膨脹為輕量級(jí)鎖,這時(shí)使用CAS操作嘗試獲得鎖。 2>如果是偏向鎖,則判斷Mark Word中的偏向線程ID是否指向當(dāng)前線程,如果偏向線程ID指向當(dāng)前線程,則表明當(dāng)前線程已經(jīng)獲取到了鎖; 3>如果偏向線程ID并未指向當(dāng)前線程,則通過(guò)CAS操作嘗試獲取偏向鎖,如果獲取鎖成功,則將Mark Word中的偏向線程ID設(shè)置為當(dāng)前線程ID; 4>如果CAS獲取偏向鎖失敗,則表示有競(jìng)爭(zhēng)。當(dāng)?shù)竭_(dá)全局安全點(diǎn)時(shí)(在這個(gè)時(shí)間點(diǎn)上沒(méi)有正在執(zhí)行的字節(jié)碼),獲得偏向鎖的線程被掛起,偏向鎖升級(jí)為輕量級(jí)鎖,然后被阻塞在安全點(diǎn)的線程繼續(xù)往下執(zhí)行同步代碼。 6)偏向鎖的釋放: 1>當(dāng)其它的線程嘗試獲取偏向鎖時(shí),持有偏向鎖的線程才會(huì)釋放偏向鎖。 2>釋放偏向鎖需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒(méi)有正在執(zhí)行的字節(jié)碼)。 3>過(guò)程: 首先暫停擁有偏向鎖的線程,然后檢查持有偏向鎖的線程是否活著,如果線程不處于活動(dòng)狀態(tài),則將對(duì)象頭設(shè)置成無(wú)鎖狀態(tài), 如果線程還活著,說(shuō)明此時(shí)發(fā)生了競(jìng)爭(zhēng),則偏向鎖升級(jí)為輕量級(jí)鎖,然后剛剛被暫停的線程會(huì)繼續(xù)往下執(zhí)行同步代碼。 7)優(yōu)點(diǎn):加鎖和解鎖不需要額外的消耗,和執(zhí)行非同步方法相比僅存在納秒級(jí)的差距 8)缺點(diǎn):如果線程間存在鎖競(jìng)爭(zhēng),鎖撤銷(xiāo)會(huì)帶來(lái)額外的消耗。 9)說(shuō)明: 1)偏向鎖默認(rèn)在應(yīng)用程序啟動(dòng)幾秒鐘之后才激活。 2)可以通過(guò)設(shè)置 -XX:BiasedLockingStartupDelay=0 來(lái)關(guān)閉延遲。 3)可以通過(guò)設(shè)置 -XX:-UseBiasedLocking=false 來(lái)關(guān)閉偏向鎖,程序默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)。(如果應(yīng)用程序里的鎖大多情況下處于競(jìng)爭(zhēng)狀態(tài),則應(yīng)該將偏向鎖關(guān)閉) 【輕量級(jí)鎖】 1)原理: 1>當(dāng)使用輕量級(jí)鎖(鎖標(biāo)識(shí)位為00)時(shí),線程在執(zhí)行同步塊之前,JVM會(huì)先在當(dāng)前線程的棧楨中創(chuàng)建用于存儲(chǔ)鎖記錄的空間,并將對(duì)象頭中的Mark Word復(fù)制到鎖記錄中(注:鎖記錄中的標(biāo)識(shí)字段稱(chēng)為Displaced Mark Word)。 2>將對(duì)象頭中的MarkWord復(fù)制到棧楨中的鎖記錄中之后,虛擬機(jī)將嘗試使用CAS將對(duì)象頭中Mark Word替換為指向該線程虛擬機(jī)棧中鎖記錄的指針,此時(shí)如果沒(méi)有線程占有鎖或者沒(méi)有線程競(jìng)爭(zhēng)鎖,則當(dāng)前線程成功獲取到鎖,然后執(zhí)行同步塊中的代碼。 3>如果在獲取到鎖的線程執(zhí)行同步代碼的過(guò)程中,另一個(gè)線程也完成了棧楨中鎖記錄的創(chuàng)建,并且已經(jīng)將對(duì)象頭中的MarkWord復(fù)制到了自己的鎖記錄中,然后嘗試使用CAS將對(duì)象頭中的MarkWord修改為指向自己的鎖記錄的指針,但是由于之前獲取到鎖的線程已經(jīng)將對(duì)象頭中的MarkWord修改過(guò)了(并且現(xiàn)在還在執(zhí)行同步體中的代碼,即仍然持有著鎖),所以此時(shí)對(duì)象頭中的MarkWord與當(dāng)前線程鎖記錄中MarkWord的值不同,導(dǎo)致CAS操作失敗,然后該線程就會(huì)不停地循環(huán)使用CAS操作試圖將對(duì)象頭中的MarkWord替換為自己鎖記錄中MarkWord的值,(當(dāng)循環(huán)次數(shù)或循環(huán)時(shí)間達(dá)到上限時(shí)停止循環(huán))如果在循環(huán)結(jié)束之前CAS操作成功,那么該線程就可以成功獲取到鎖,如果循環(huán)結(jié)束之后依然獲取不到鎖,則鎖獲取失敗,對(duì)象頭中的MarkWord會(huì)被修改為指向重量級(jí)鎖的指針,然后這個(gè)獲取鎖失敗的線程就會(huì)被掛起,阻塞了。 4>當(dāng)持有鎖的那個(gè)線程執(zhí)行完同步體之后,使用CAS操作將對(duì)象頭中的MarkWord還原為最初的狀態(tài)時(shí)(將對(duì)象頭中指向鎖記錄的指針替換為Displaced Mark Word ),發(fā)現(xiàn)MarkWord已被修改為指向重量級(jí)鎖的指針,因此CAS操作失敗,該線程會(huì)釋放鎖并喚起阻塞等待的線程,開(kāi)始新一輪奪鎖之爭(zhēng),而此時(shí),輕量級(jí)鎖已經(jīng)膨脹為重量級(jí)鎖,所有競(jìng)爭(zhēng)失敗的線程都會(huì)阻塞,而不是自旋。 自旋鎖: 1)所謂自旋鎖,就是讓沒(méi)有獲得鎖的進(jìn)程自己運(yùn)行一段時(shí)間自循環(huán)(默認(rèn)開(kāi)啟),但是不掛起線程。 2)自旋的代價(jià)就是該線程會(huì)一直占用處理器如果鎖占用的時(shí)間很短,自旋等待的效果很好,反之,自旋鎖會(huì)消耗大量處理器資源。 3)因此,自旋的等待時(shí)間必須有一定限度,超過(guò)限度還沒(méi)有獲得鎖,就要掛起線程。 優(yōu)點(diǎn):在沒(méi)有多線程競(jìng)爭(zhēng)的前提下,減少傳統(tǒng)的重量級(jí)鎖帶來(lái)的性能損耗。 缺點(diǎn):競(jìng)爭(zhēng)的線程如果始終得不到鎖,自旋會(huì)消耗cpu。 應(yīng)用:追求響應(yīng)時(shí)間,同步塊執(zhí)行速度非??臁?nbsp; 【重量級(jí)鎖】 說(shuō)明: 1)java6之前的synchronized屬于重量級(jí)鎖,效率低下,因?yàn)閙onitor是依賴(lài)操作系統(tǒng)的Mutex Lock(互斥量)來(lái)實(shí)現(xiàn)的。 2)多線程競(jìng)爭(zhēng)鎖時(shí),會(huì)引起線程的上下文切換(即在cpu分配的時(shí)間片還沒(méi)有用完的情況下進(jìn)行了上下文切換)。 3)操作系統(tǒng)實(shí)現(xiàn)線程的上下文切換需要從用戶(hù)態(tài)轉(zhuǎn)換到核心態(tài),這個(gè)狀態(tài)之間的轉(zhuǎn)換需要相對(duì)較長(zhǎng)的時(shí)間,時(shí)間成本相對(duì)較高。 4)在互斥狀態(tài)下,沒(méi)有得到鎖的線程會(huì)被掛起阻塞,而掛起線程和恢復(fù)線程的操作都需要從用戶(hù)態(tài)轉(zhuǎn)入內(nèi)核態(tài)中完成。 優(yōu)點(diǎn):線程競(jìng)爭(zhēng)不使用自旋,不會(huì)消耗cpu。 缺點(diǎn):線程阻塞,響應(yīng)時(shí)間緩慢。 應(yīng)用:追求吞吐量,同步塊執(zhí)行速度較長(zhǎng)
感謝各位的閱讀,以上就是“java中鎖的優(yōu)化方法”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)java中鎖的優(yōu)化方法這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。