最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺談Java虛擬機(jī)對(duì)內(nèi)部鎖的四種優(yōu)化方式

 更新時(shí)間:2017年10月12日 11:08:38   作者:博文視點(diǎn)  
這篇文章主要介紹了淺談Java虛擬機(jī)對(duì)內(nèi)部鎖的四種優(yōu)化方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

自Java 6/Java 7開始,Java虛擬機(jī)對(duì)內(nèi)部鎖的實(shí)現(xiàn)進(jìn)行了一些優(yōu)化。這些優(yōu)化主要包括鎖消除(Lock Elision)、鎖粗化(Lock Coarsening)、偏向鎖(Biased Locking)以及適應(yīng)性鎖(Adaptive Locking)。這些優(yōu)化僅在Java虛擬機(jī)server模式下起作用(即運(yùn)行Java程序時(shí)我們可能需要在命令行中指定Java虛擬機(jī)參數(shù)“-server”以開啟這些優(yōu)化)。

1 鎖消除

鎖消除(Lock Elision)是JIT編譯器對(duì)內(nèi)部鎖的具體實(shí)現(xiàn)所做的一種優(yōu)化。

 

鎖消除(Lock Elision)示意圖

在動(dòng)態(tài)編譯同步塊的時(shí)候,JIT編譯器可以借助一種被稱為逃逸分析(Escape Analysis)的技術(shù)來判斷同步塊所使用的鎖對(duì)象是否只能夠被一個(gè)線程訪問而沒有被發(fā)布到其他線程。如果同步塊所使用的鎖對(duì)象通過這種分析被證實(shí)只能夠被一個(gè)線程訪問,那么JIT編譯器在編譯這個(gè)同步塊的時(shí)候并不生成synchronized所表示的鎖的申請(qǐng)與釋放對(duì)應(yīng)的機(jī)器碼,而僅生成原臨界區(qū)代碼對(duì)應(yīng)的機(jī)器碼,這就造成了被動(dòng)態(tài)編譯的字節(jié)碼就像是不包含monitorenter(申請(qǐng)鎖)和monitorexit(釋放鎖)這兩個(gè)字節(jié)碼指令一樣,即消除了鎖的使用。這種編譯器優(yōu)化就被稱為鎖消除(Lock Elision),它使得特定情況下我們可以完全消除鎖的開銷。

Java標(biāo)準(zhǔn)庫中的有些類(比如StringBuffer)雖然是線程安全的,但是在實(shí)際使用中我們往往不在多個(gè)線程間共享這些類的實(shí)例。而這些類在實(shí)現(xiàn)線程安全的時(shí)候往往借助于內(nèi)部鎖。因此,這些類是鎖消除優(yōu)化的常見目標(biāo)。

清單12-1  可進(jìn)行鎖消除優(yōu)化的示例代碼

public class LockElisionExample {

 public static String toJSON(ProductInfo productInfo) {
  StringBuffer sbf = new StringBuffer();
  sbf.append("{\"productID\":\"").append(productInfo.productID);
  sbf.append("\",\"categoryID\":\"").append(productInfo.categoryID);
  sbf.append("\",\"rank\":").append(productInfo.rank);
  sbf.append(",\"inventory\":").append(productInfo.inventory);
  sbf.append('}');

  return sbf.toString();
 }
}

在上面例子中,JIT編譯器在編譯toJSON方法的時(shí)候會(huì)將其調(diào)用的StringBuffer.append/toString方法內(nèi)聯(lián)(Inline)到該方法之中,這相當(dāng)于把StringBuffer.append/toString方法的方法體中的指令復(fù)制到toJSON方法體之中。這里的StringBuffer實(shí)例sbf是一個(gè)局部變量,并且該變量所引用的對(duì)象并沒有被發(fā)布到其他線程,因此sbf引用的對(duì)象只能夠被sbf所在的方法(toJSON方法)的當(dāng)前執(zhí)行線程(一個(gè)線程)訪問。所以,JIT編譯器此時(shí)可以消除toJSON方法中從StringBuffer.append/toString方法的方法體復(fù)制的指令所使用的內(nèi)部鎖。在這個(gè)例子中,StringBuffer.append/toString方法本身所使用的鎖并不會(huì)被消除,因?yàn)橄到y(tǒng)中可能還有其他地方在使用StringBuffer,而這些代碼可能會(huì)共享StringBuffer實(shí)例。

鎖消除優(yōu)化所依賴的逃逸分析技術(shù)自Java SE 6u23起默認(rèn)是開啟的,但是鎖消除優(yōu)化是在Java 7開始引入的。

從上述例子可以看出,鎖消除優(yōu)化還可能需要以JIT編譯器的內(nèi)聯(lián)優(yōu)化為前提。而一個(gè)方法是否會(huì)被JIT編譯器內(nèi)聯(lián)取決于該方法的熱度以及該方法對(duì)應(yīng)的字節(jié)碼的尺寸(Bytecode Size)。因此,鎖消除優(yōu)化能否被實(shí)施還取決于被調(diào)用的同步方法(或者帶同步塊的方法)是否能夠被內(nèi)聯(lián)。

鎖消除優(yōu)化告訴我們?cè)谠撌褂面i的情況下必須使用鎖,而不必過多在意鎖的開銷。開發(fā)人員應(yīng)該在代碼的邏輯層面考慮是否需要加鎖,而至于代碼運(yùn)行層面上某個(gè)鎖是否真的有必要使用則由JIT編譯器來決定。鎖消除優(yōu)化并不表示開發(fā)人員在編寫代碼的時(shí)候可以隨意使用內(nèi)部鎖(在不需要加鎖的情況下加鎖),因?yàn)殒i消除是JIT編譯器而不是javac所做的一種優(yōu)化,而一段代碼只有在其被執(zhí)行的頻率足夠大的情況下才有可能會(huì)被JIT編譯器優(yōu)化。也就是說在JIT編譯器優(yōu)化介入之前,只要源代碼中使用了內(nèi)部鎖,那么這個(gè)鎖的開銷就會(huì)存在。另外,JIT編譯器所執(zhí)行的內(nèi)聯(lián)優(yōu)化、逃逸分析以及鎖消除優(yōu)化本身都是有其開銷的。

在鎖消除的作用下,利用ThreadLocal將一個(gè)線程安全的對(duì)象(比如Random)作為一個(gè)線程特有對(duì)象來使用,不僅僅可以避免鎖的爭用,還可以徹底消除這些對(duì)象內(nèi)部所使用的鎖的開銷。

2 鎖粗化

鎖粗化(Lock Coarsening/Lock Merging)是JIT編譯器對(duì)內(nèi)部鎖的具體實(shí)現(xiàn)所做的一種優(yōu)化。

 

鎖粗化(Lock Coarsening)示意圖

對(duì)于相鄰的幾個(gè)同步塊,如果這些同步塊使用的是同一個(gè)鎖實(shí)例,那么JIT編譯器會(huì)將這些同步塊合并為一個(gè)大同步塊,從而避免了一個(gè)線程反復(fù)申請(qǐng)、釋放同一個(gè)鎖所導(dǎo)致的開銷。然而,鎖粗化可能導(dǎo)致一個(gè)線程持續(xù)持有一個(gè)鎖的時(shí)間變長,從而使得同步在該鎖之上的其他線程在申請(qǐng)鎖時(shí)的等待時(shí)間變長。例如上圖中,第1個(gè)同步塊結(jié)束和第2個(gè)同步塊開始之間的時(shí)間間隙中,其他線程本來是有機(jī)會(huì)獲得monitorX的,但是經(jīng)過鎖粗化之后由于臨界區(qū)的長度變長,這些線程在申請(qǐng)monitorX時(shí)所需的等待時(shí)間也相應(yīng)變長了。因此,鎖粗化不會(huì)被應(yīng)用到循環(huán)體內(nèi)的相鄰?fù)綁K。

相鄰的兩個(gè)同步塊之間如果存在其他語句,也不一定就會(huì)阻礙JIT編譯器執(zhí)行鎖粗化優(yōu)化,這是因?yàn)镴IT編譯器可能在執(zhí)行鎖粗化優(yōu)化前將這些語句挪到(即指令重排序)后一個(gè)同步塊的臨界區(qū)之中(當(dāng)然,JIT編譯器并不會(huì)將臨界區(qū)內(nèi)的代碼挪到臨界區(qū)之外)。

實(shí)際上,我們寫的代碼中可能很少會(huì)出現(xiàn)上圖中那種連續(xù)的同步塊。這種同一個(gè)鎖實(shí)例引導(dǎo)的相鄰?fù)綁K往往是JIT編譯器編譯之后形成的。

例如,在下面的例子中

清單12-2  可進(jìn)行鎖粗化優(yōu)化的示例代碼

public class LockCoarseningExample {
 private final Random rnd = new Random();

 public void simulate() {
  int iq1 = randomIQ();
  int iq2 = randomIQ();
  int iq3 = randomIQ();
  act(iq1, iq2, iq3);
 }

 private void act(int... n) {
  // ...
 }

 // 返回隨機(jī)的智商值
 public int randomIQ() {
  // 人類智商的標(biāo)準(zhǔn)差是15,平均值是100
  return (int) Math.round(rnd.nextGaussian() * 15 + 100);
 }
 // ...
}

simulate方法連續(xù)調(diào)用randomIQ方法來生成3個(gè)符合正態(tài)分布(高斯分布)的隨機(jī)智商(IQ)。在simulate方法被執(zhí)行得足夠頻繁的情況下,JIT編譯器可能對(duì)該方法執(zhí)行一系優(yōu)化:首先,JIT編譯器可能將randomIQ方法內(nèi)聯(lián)(inline)到simulate方法中,這相當(dāng)于把randomIQ方法體中的指令復(fù)制到simulate方法之中。在此基礎(chǔ)上,randomIQ方法中的rnd.nextGaussian()調(diào)用也可能被內(nèi)聯(lián),這相當(dāng)于把Random.nextGaussian()方法體中的指令復(fù)制到simulate方法之中。Random.nextGaussian()是一個(gè)同步方法,由于Random實(shí)例rnd可能被多個(gè)線程共享(因?yàn)閟imulate方法可能被多個(gè)線程執(zhí)行),因此JIT編譯器無法對(duì)Random.nextGaussian()方法本身執(zhí)行鎖消除優(yōu)化,這使得被內(nèi)聯(lián)到simulate方法中的Random.nextGaussian()方法體相當(dāng)于一個(gè)由rnd引導(dǎo)的同步塊。經(jīng)過上述優(yōu)化之后,JIT編譯器便會(huì)發(fā)現(xiàn)simulate方法中存在3個(gè)相鄰的由rnd(Random實(shí)例)引導(dǎo)的同步塊,于是鎖粗化優(yōu)化便“粉墨登場(chǎng)”了。

鎖粗化默認(rèn)是開啟的。如果要關(guān)閉這個(gè)特性,我們可以在Java程序的啟動(dòng)命令行中添加虛擬機(jī)參數(shù)“-XX:-EliminateLocks”(開啟則可以使用虛擬機(jī)參數(shù)“-XX:+EliminateLocks”)。

3 偏向鎖

偏向鎖(Biased Locking)是Java虛擬機(jī)對(duì)鎖的實(shí)現(xiàn)所做的一種優(yōu)化。這種優(yōu)化基于這樣的觀測(cè)結(jié)果(Observation):大多數(shù)鎖并沒有被爭用(Contented),并且這些鎖在其整個(gè)生命周期內(nèi)至多只會(huì)被一個(gè)線程持有。然而,Java虛擬機(jī)在實(shí)現(xiàn)monitorenter字節(jié)碼(申請(qǐng)鎖)和monitorexit字節(jié)碼(釋放鎖)時(shí)需要借助一個(gè)原子操作(CAS操作),這個(gè)操作代價(jià)相對(duì)來說比較昂貴。因此,Java虛擬機(jī)會(huì)為每個(gè)對(duì)象維護(hù)一個(gè)偏好(Bias),即一個(gè)對(duì)象對(duì)應(yīng)的內(nèi)部鎖第1次被一個(gè)線程獲得,那么這個(gè)線程就會(huì)被記錄為該對(duì)象的偏好線程(Biased Thread)。這個(gè)線程后續(xù)無論是再次申請(qǐng)?jiān)撴i還是釋放該鎖,都無須借助原先(指未實(shí)施偏向鎖優(yōu)化前)昂貴的原子操作,從而減少了鎖的申請(qǐng)與釋放的開銷。

然而,一個(gè)鎖沒有被爭用并不代表僅僅只有一個(gè)線程訪問該鎖,當(dāng)一個(gè)對(duì)象的偏好線程以外的其他線程申請(qǐng)?jiān)搶?duì)象的內(nèi)部鎖時(shí),Java虛擬機(jī)需要收回(Revoke)該對(duì)象對(duì)原偏好線程的“偏好”并重新設(shè)置該對(duì)象的偏好線程。這個(gè)偏好收回和重新分配過程的代價(jià)也是比較昂貴的,因此如果程序運(yùn)行過程中存在比較多的鎖爭用的情況,那么這種偏好收回和重新分配的代價(jià)便會(huì)被放大。有鑒于此,偏向鎖優(yōu)化只適合于存在相當(dāng)大一部分鎖并沒有被爭用的系統(tǒng)之中。如果系統(tǒng)中存在大量被爭用的鎖而沒有被爭用的鎖僅占極小的部分,那么我們可以考慮關(guān)閉偏向鎖優(yōu)化。

偏向鎖優(yōu)化默認(rèn)是開啟的。要關(guān)閉偏向鎖優(yōu)化,我們可以在Java程序的啟動(dòng)命令行中添加虛擬機(jī)參數(shù)“-XX:-UseBiasedLocking”(開啟偏向鎖優(yōu)化可以使用虛擬機(jī)參數(shù)“-XX:+UseBiasedLocking”)。

4 適應(yīng)性鎖

適應(yīng)性鎖(Adaptive Locking,也被稱為 Adaptive Spinning )是JIT編譯器對(duì)內(nèi)部鎖實(shí)現(xiàn)所做的一種優(yōu)化。

存在鎖爭用的情況下,一個(gè)線程申請(qǐng)一個(gè)鎖的時(shí)候如果這個(gè)鎖恰好被其他線程持有,那么這個(gè)線程就需要等待該鎖被其持有線程釋放。實(shí)現(xiàn)這種等待的一種保守方法——將這個(gè)線程暫停(線程的生命周期狀態(tài)變?yōu)榉荝unnable狀態(tài))。由于暫停線程會(huì)導(dǎo)致上下文切換,因此對(duì)于一個(gè)具體鎖實(shí)例來說,這種實(shí)現(xiàn)策略比較適合于系統(tǒng)中絕大多數(shù)線程對(duì)該鎖的持有時(shí)間較長的場(chǎng)景,這樣才能夠抵消上下文切換的開銷。另外一種實(shí)現(xiàn)方法就是采用忙等(Busy Wait)。所謂忙等相當(dāng)于如下代碼所示的一個(gè)循環(huán)體為空的循環(huán)語句:

// 當(dāng)鎖被其他線程持有時(shí)一直循環(huán) 
while (lockIsHeldByOtherThread){} 

可見,忙等是通過反復(fù)執(zhí)行空操作(什么也不做)直到所需的條件成立為止而實(shí)現(xiàn)等待的。這種策略的好處是不會(huì)導(dǎo)致上下文切換,缺點(diǎn)是比較耗費(fèi)處理器資源——如果所需的條件在相當(dāng)長時(shí)間內(nèi)未能成立,那么忙等的循環(huán)就會(huì)一直被執(zhí)行。因此,對(duì)于一個(gè)具體的鎖實(shí)例來說,忙等策略比較適合于絕大多數(shù)線程對(duì)該鎖的持有時(shí)間較短的場(chǎng)景,這樣能夠避免過多的處理器時(shí)間開銷。

事實(shí)上,Java虛擬機(jī)也不是非要在上述兩種實(shí)現(xiàn)策略之中擇其一 ——它可以綜合使用上述兩種策略。對(duì)于一個(gè)具體的鎖實(shí)例,Java虛擬機(jī)會(huì)根據(jù)其運(yùn)行過程中收集到的信息來判斷這個(gè)鎖是屬于被線程持有時(shí)間“較長”的還是“較短”的。對(duì)于被線程持有時(shí)間“較長”的鎖,Java虛擬機(jī)會(huì)選用暫停等待策略;而對(duì)于被線程持有時(shí)間“較短”的鎖,Java虛擬機(jī)會(huì)選用忙等等待策略。Java虛擬機(jī)也可能先采用忙等等待策略,在忙等失敗的情況下再采用暫停等待策略。Java虛擬機(jī)的這種優(yōu)化就被稱為適應(yīng)性鎖(Adaptive Locking),這種優(yōu)化同樣也需要JIT編譯器介入。

適應(yīng)性鎖優(yōu)化可以是以具體的一個(gè)鎖實(shí)例為基礎(chǔ)的。也就是說,Java虛擬機(jī)可能對(duì)一個(gè)鎖實(shí)例采用忙等等待策略,而對(duì)另外一個(gè)鎖實(shí)例采用暫停等待策略。

從適應(yīng)性鎖優(yōu)化可以看出,內(nèi)部鎖的使用并不一定會(huì)導(dǎo)致上下文切換,這就是我們說鎖與上下文切換時(shí)均說鎖“可能”導(dǎo)致上下文切換的原因。

本文選自《Java多線程編程實(shí)戰(zhàn)指南(核心篇)》。

 

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家?!             ?/p>

相關(guān)文章

  • Java修改PowerPoint幻燈片批注信息

    Java修改PowerPoint幻燈片批注信息

    這篇文章主要介紹了Java修改PowerPoint幻燈片批注信息,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • springBoot controller,service,dao,mapper,model層的作用說明

    springBoot controller,service,dao,mapper,model層的作用說明

    這篇文章主要介紹了springBoot controller,service,dao,mapper,model層的作用說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java基礎(chǔ)精講方法的使用

    Java基礎(chǔ)精講方法的使用

    方法,也稱函數(shù),如果想要重復(fù)一段或者多段代碼塊的使用,可以將這些代碼封裝成一個(gè)方法,方法具體表現(xiàn)為某種行為,使用方法可以提高代碼的復(fù)用性
    2022-05-05
  • Spring Boot搭配MinIO和KKFileView實(shí)現(xiàn)文件存儲(chǔ)和在線預(yù)覽

    Spring Boot搭配MinIO和KKFileView實(shí)現(xiàn)文件存儲(chǔ)和在線預(yù)覽

    在現(xiàn)代的Web應(yīng)用中,文件上傳和預(yù)覽是常見的需求場(chǎng)景,尤其是在內(nèi)容管理系統(tǒng)(CMS)或企業(yè)內(nèi)部應(yīng)用,本文介紹了如何使用SpringBoot、MinIO和KKFileView實(shí)現(xiàn)文件上傳和在線預(yù)覽功能,包括項(xiàng)目背景、技術(shù)選型、環(huán)境準(zhǔn)備、項(xiàng)目代碼實(shí)現(xiàn)和運(yùn)行測(cè)試等步驟
    2024-12-12
  • Java+mysql本地圖片上傳數(shù)據(jù)庫及下載示例

    Java+mysql本地圖片上傳數(shù)據(jù)庫及下載示例

    本篇文章主要介紹了Java+mysql本地圖片上傳數(shù)據(jù)庫及下載示例,具有一定的參加價(jià)值,有興趣的可以了解一下。
    2017-01-01
  • 圖解Java經(jīng)典算法歸并排序的原理與實(shí)現(xiàn)

    圖解Java經(jīng)典算法歸并排序的原理與實(shí)現(xiàn)

    歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide?and?Conquer)的一個(gè)非常典型的應(yīng)用。本文將通過動(dòng)圖詳解歸并排序的原理及實(shí)現(xiàn),需要的可以參考一下
    2022-09-09
  • 詳解Spark?Sql在UDF中如何引用外部數(shù)據(jù)

    詳解Spark?Sql在UDF中如何引用外部數(shù)據(jù)

    這篇文章主要為大家介紹了詳解Spark?Sql在UDF中如何引用外部數(shù)據(jù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • 深入理解Java三大特性中的多態(tài)

    深入理解Java三大特性中的多態(tài)

    多態(tài)性是對(duì)象多種表現(xiàn)形式的體現(xiàn)。在面向?qū)ο笾?,最常見的多態(tài)發(fā)生在使用父類的引用來引用子類的對(duì)象。下面這篇文章主要給大家深入的介紹了Java三大特性中多態(tài)的相關(guān)資料,有需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-01-01
  • java設(shè)計(jì)模式之外觀模式(Facade)

    java設(shè)計(jì)模式之外觀模式(Facade)

    這篇文章主要為大家詳細(xì)介紹了java設(shè)計(jì)模式之外觀模式Facade的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • 搭建簡單的Spring-Data JPA項(xiàng)目

    搭建簡單的Spring-Data JPA項(xiàng)目

    本文主要介紹了搭建簡單的Spring-Data JPA項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02

最新評(píng)論

郑州市| 台中县| 塘沽区| 老河口市| 渭源县| 古丈县| 乐亭县| 安徽省| 如东县| 九龙城区| 尼玛县| 伊金霍洛旗| 博罗县| 墨竹工卡县| 介休市| 开江县| 定日县| 永靖县| 从化市| 宜昌市| 淮阳县| 泗洪县| 鹤壁市| 姜堰市| 日土县| 舞钢市| 克什克腾旗| 三明市| 博客| 平度市| 葫芦岛市| 新源县| 昌邑市| 公安县| 和政县| 武强县| 沂源县| 南雄市| 安远县| 波密县| 华池县|