這篇文章主要介紹“Java鎖如何優(yōu)化”,在日常操作中,相信很多人在Java鎖如何優(yōu)化問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java鎖如何優(yōu)化”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!
創(chuàng)新互聯(lián)建站是專業(yè)的東寶網(wǎng)站建設(shè)公司,東寶接單;提供成都網(wǎng)站建設(shè)、網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行東寶網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
鎖優(yōu)化
這里的鎖優(yōu)化主要是指 JVM 對 synchronized 的優(yōu)化。
自旋鎖
互斥同步進入阻塞狀態(tài)的開銷都很大,應(yīng)該盡量避免。在許多應(yīng)用中,共享數(shù)據(jù)的鎖定狀態(tài)只會持續(xù)很短的一段時間。自旋鎖的思想是讓一個線程在請求一個共享數(shù)據(jù)的鎖時執(zhí)行忙循環(huán)(自旋)一段時間,如果在這段時間內(nèi)能獲得鎖,就可以避免進入阻塞狀態(tài)。
自旋鎖雖然能避免進入阻塞狀態(tài)從而減少開銷,但是它需要進行忙循環(huán)操作占用 CPU 時間,它只適用于共享數(shù)據(jù)的鎖定狀態(tài)很短的場景。
在 JDK 1.6 中引入了自適應(yīng)的自旋鎖。自適應(yīng)意味著自旋的次數(shù)不再固定了,而是由前一次在同一個鎖上的自旋次數(shù)及鎖的擁有者的狀態(tài)來決定。
鎖消除
鎖消除是指對于被檢測出不可能存在競爭的共享數(shù)據(jù)的鎖進行消除。
鎖消除主要是通過逃逸分析來支持,如果堆上的共享數(shù)據(jù)不可能逃逸出去被其它線程訪問到,那么就可以把它們當(dāng)成私有數(shù)據(jù)對待,也就可以將它們的鎖進行消除。
對于一些看起來沒有加鎖的代碼,其實隱式的加了很多鎖。例如下面的字符串拼接代碼就隱式加了鎖:
public static String concatString(String s1, String s2, String s3) { return s1 + s2 + s3; }
String 是一個不可變的類,編譯器會對 String 的拼接自動優(yōu)化。在 JDK 1.5 之前,會轉(zhuǎn)化為 StringBuffer 對象的連續(xù) append() 操作:
public static String concatString(String s1, String s2, String s3) { StringBuffer sb = new StringBuffer(); sb.append(s1); sb.append(s2); sb.append(s3); return sb.toString(); }
每個 append() 方法中都有一個同步塊。虛擬機觀察變量 sb,很快就會發(fā)現(xiàn)它的動態(tài)作用域被限制在 concatString() 方法內(nèi)部。也就是說,sb 的所有引用永遠(yuǎn)不會逃逸到 concatString() 方法之外,其他線程無法訪問到它,因此可以進行消除。
鎖粗化
如果一系列的連續(xù)操作都對同一個對象反復(fù)加鎖和解鎖,頻繁的加鎖操作就會導(dǎo)致性能損耗。
上一節(jié)的示例代碼中連續(xù)的 append() 方法就屬于這類情況。如果虛擬機探測到由這樣的一串零碎的操作都對同一個對象加鎖,將會把加鎖的范圍擴展(粗化)到整個操作序列的外部。對于上一節(jié)的示例代碼就是擴展到第一個 append() 操作之前直至最后一個 append() 操作之后,這樣只需要加鎖一次就可以了。
輕量級鎖
JDK 1.6 引入了偏向鎖和輕量級鎖,從而讓鎖擁有了四個狀態(tài):無鎖狀態(tài)(unlocked)、偏向鎖狀態(tài)(biasble)、輕量級鎖狀態(tài)(lightweight locked)和重量級鎖狀態(tài)(inflated)。
重量級鎖也就是通常說synchronized的對象鎖。
以下是 HotSpot 虛擬機對象頭的內(nèi)存布局,這些數(shù)據(jù)被稱為 Mark Word。其中 tag bits 對應(yīng)了五個狀態(tài),這些狀態(tài)在右側(cè)的 state 表格中給出。除了 marked for gc 狀態(tài),其它四個狀態(tài)已經(jīng)在前面介紹過了。
下圖左側(cè)是一個線程的虛擬機棧,其中有一部分稱為 Lock Record 的區(qū)域,這是在輕量級鎖運行過程創(chuàng)建的,用于存放鎖對象的 Mark Word。而右側(cè)就是一個鎖對象,包含了 Mark Word 和其它信息。
輕量級鎖是相對于傳統(tǒng)的重量級鎖而言,它使用 CAS 操作來避免重量級鎖使用互斥量的開銷。對于絕大部分的鎖,在整個同步周期內(nèi)都是不存在競爭的,因此也就不需要都使用互斥量進行同步,可以先采用 CAS 操作進行同步,如果 CAS 失敗了再改用互斥量進行同步。
當(dāng)嘗試獲取一個鎖對象時,如果鎖對象標(biāo)記為 0 01,說明鎖對象的鎖未鎖定(unlocked)狀態(tài)。此時虛擬機在當(dāng)前線程的虛擬機棧中創(chuàng)建 Lock Record,然后使用 CAS 操作將對象的 Mark Word 更新為 Lock Record 指針。如果 CAS 操作成功了,那么線程就獲取了該對象上的鎖,并且對象的 Mark Word 的鎖標(biāo)記變?yōu)?00,表示該對象處于輕量級鎖狀態(tài)。
如果 CAS 操作失敗了,虛擬機首先會檢查對象的 Mark Word 是否指向當(dāng)前線程的虛擬機棧,如果是的話說明當(dāng)前線程已經(jīng)擁有了這個鎖對象,那就可以直接進入同步塊繼續(xù)執(zhí)行,否則說明這個鎖對象已經(jīng)被其他線程線程搶占了。如果有兩條以上的線程爭用同一個鎖,那輕量級鎖就不再有效,要膨脹為重量級鎖。
偏向鎖
偏向鎖的思想是偏向于第一個獲取鎖對象的線程,這個線程在之后獲取該鎖就不再需要進行同步操作,甚至連 CAS 操作也不再需要。
當(dāng)鎖對象第一次被線程獲得的時候,進入偏向狀態(tài),標(biāo)記為 1 01。同時使用 CAS 操作將線程 ID 記錄到 Mark Word 中,如果 CAS 操作成功,這個線程以后每次進入這個鎖相關(guān)的同步塊就不需要再進行任何同步操作。
當(dāng)有另外一個線程去嘗試獲取這個鎖對象時,偏向狀態(tài)就宣告結(jié)束,此時撤銷偏向(Revoke Bias)后恢復(fù)到未鎖定狀態(tài)或者輕量級鎖狀態(tài)。
到此,關(guān)于“Java鎖如何優(yōu)化”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
分享標(biāo)題:Java鎖如何優(yōu)化
本文來源:http://weahome.cn/article/gigeds.html