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

Java 并發(fā)編程學習筆記之Synchronized底層優(yōu)化

 更新時間:2016年05月17日 09:30:55   作者:liuxiaopeng  
這篇文章主要介紹了Java 并發(fā)編程學習筆記之Synchronized底層優(yōu)化的相關資料,主要包含了重量級鎖,輕量級鎖,偏向鎖和其他優(yōu)化等方面,有需要的小伙伴可以參考下

一、重量級鎖

  上篇文章中向大家介紹了Synchronized的用法及其實現(xiàn)的原理?,F(xiàn)在我們應該知道,Synchronized是通過對象內(nèi)部的一個叫做監(jiān)視器鎖(monitor)來實現(xiàn)的。但是監(jiān)視器鎖本質(zhì)又是依賴于底層的操作系統(tǒng)的Mutex Lock來實現(xiàn)的。而操作系統(tǒng)實現(xiàn)線程之間的切換這就需要從用戶態(tài)轉(zhuǎn)換到核心態(tài),這個成本非常高,狀態(tài)之間的轉(zhuǎn)換需要相對比較長的時間,這就是為什么Synchronized效率低的原因。因此,這種依賴于操作系統(tǒng)Mutex Lock所實現(xiàn)的鎖我們稱之為“重量級鎖”。JDK中對Synchronized做的種種優(yōu)化,其核心都是為了減少這種重量級鎖的使用。JDK1.6以后,為了減少獲得鎖和釋放鎖所帶來的性能消耗,提高性能,引入了“輕量級鎖”和“偏向鎖”。

二、輕量級鎖

  鎖的狀態(tài)總共有四種:無鎖狀態(tài)、偏向鎖、輕量級鎖和重量級鎖。隨著鎖的競爭,鎖可以從偏向鎖升級到輕量級鎖,再升級的重量級鎖(但是鎖的升級是單向的,也就是說只能從低到高升級,不會出現(xiàn)鎖的降級)。JDK 1.6中默認是開啟偏向鎖和輕量級鎖的,我們也可以通過-XX:-UseBiasedLocking來禁用偏向鎖。鎖的狀態(tài)保存在對象的頭文件中,以32位的JDK為例:

鎖狀態(tài)

25 bit

4bit

1bit

2bit

23bit

2bit

是否是偏向鎖

鎖標志位

輕量級鎖

指向棧中鎖記錄的指針

00

重量級鎖

指向互斥量(重量級鎖)的指針

10

GC標記

11

偏向鎖

線程ID

Epoch

對象分代年齡

1

01

無鎖

對象的hashCode

對象分代年齡

0

01


  “輕量級”是相對于使用操作系統(tǒng)互斥量來實現(xiàn)的傳統(tǒng)鎖而言的。但是,首先需要強調(diào)一點的是,輕量級鎖并不是用來代替重量級鎖的,它的本意是在沒有多線程競爭的前提下,減少傳統(tǒng)的重量級鎖使用產(chǎn)生的性能消耗。在解釋輕量級鎖的執(zhí)行過程之前,先明白一點,輕量級鎖所適應的場景是線程交替執(zhí)行同步塊的情況,如果存在同一時間訪問同一鎖的情況,就會導致輕量級鎖膨脹為重量級鎖。

1、輕量級鎖的加鎖過程

 ?。?)在代碼進入同步塊的時候,如果同步對象鎖狀態(tài)為無鎖狀態(tài)(鎖標志位為“01”狀態(tài),是否為偏向鎖為“0”),虛擬機首先將在當前線程的棧幀中建立一個名為鎖記錄(Lock Record)的空間,用于存儲鎖對象目前的Mark Word的拷貝,官方稱之為 Displaced Mark Word。這時候線程堆棧與對象頭的狀態(tài)如圖2.1所示。

 ?。?)拷貝對象頭中的Mark Word復制到鎖記錄中。

  (3)拷貝成功后,虛擬機將使用CAS操作嘗試將對象的Mark Word更新為指向Lock Record的指針,并將Lock record里的owner指針指向object mark word。如果更新成功,則執(zhí)行步驟(3),否則執(zhí)行步驟(4)。

  (4)如果這個更新動作成功了,那么這個線程就擁有了該對象的鎖,并且對象Mark Word的鎖標志位設置為“00”,即表示此對象處于輕量級鎖定狀態(tài),這時候線程堆棧與對象頭的狀態(tài)如圖2.2所示。

 ?。?)如果這個更新操作失敗了,虛擬機首先會檢查對象的Mark Word是否指向當前線程的棧幀,如果是就說明當前線程已經(jīng)擁有了這個對象的鎖,那就可以直接進入同步塊繼續(xù)執(zhí)行。否則說明多個線程競爭鎖,輕量級鎖就要膨脹為重量級鎖,鎖標志的狀態(tài)值變?yōu)椤?0”,Mark Word中存儲的就是指向重量級鎖(互斥量)的指針,后面等待鎖的線程也要進入阻塞狀態(tài)。 而當前線程便嘗試使用自旋來獲取鎖,自旋就是為了不讓線程阻塞,而采用循環(huán)去獲取鎖的過程。

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

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

2、輕量級鎖的解鎖過程:

 ?。?)通過CAS操作嘗試把線程中復制的Displaced Mark Word對象替換當前的Mark Word。

 ?。?)如果替換成功,整個同步過程就完成了。

 ?。?)如果替換失敗,說明有其他線程嘗試過獲取該鎖(此時鎖已膨脹),那就要在釋放鎖的同時,喚醒被掛起的線程。

三、偏向鎖

  引入偏向鎖是為了在無多線程競爭的情況下盡量減少不必要的輕量級鎖執(zhí)行路徑,因為輕量級鎖的獲取及釋放依賴多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時候依賴一次CAS原子指令(由于一旦出現(xiàn)多線程競爭的情況就必須撤銷偏向鎖,所以偏向鎖的撤銷操作的性能損耗必須小于節(jié)省下來的CAS原子指令的性能消耗)。上面說過,輕量級鎖是為了在線程交替執(zhí)行同步塊時提高性能,而偏向鎖則是在只有一個線程執(zhí)行同步塊時進一步提高性能。

1、偏向鎖獲取過程:

 ?。?)訪問Mark Word中偏向鎖的標識是否設置成1,鎖標志位是否為01——確認為可偏向狀態(tài)。

 ?。?)如果為可偏向狀態(tài),則測試線程ID是否指向當前線程,如果是,進入步驟(5),否則進入步驟(3)。

 ?。?)如果線程ID并未指向當前線程,則通過CAS操作競爭鎖。如果競爭成功,則將Mark Word中線程ID設置為當前線程ID,然后執(zhí)行(5);如果競爭失敗,執(zhí)行(4)。

 ?。?)如果CAS獲取偏向鎖失敗,則表示有競爭。當?shù)竭_全局安全點(safepoint)時獲得偏向鎖的線程被掛起,偏向鎖升級為輕量級鎖,然后被阻塞在安全點的線程繼續(xù)往下執(zhí)行同步代碼。

 ?。?)執(zhí)行同步代碼。

2、偏向鎖的釋放:

  偏向鎖的撤銷在上述第四步驟中有提到。偏向鎖只有遇到其他線程嘗試競爭偏向鎖時,持有偏向鎖的線程才會釋放鎖,線程不會主動去釋放偏向鎖。偏向鎖的撤銷,需要等待全局安全點(在這個時間點上沒有字節(jié)碼正在執(zhí)行),它會首先暫停擁有偏向鎖的線程,判斷鎖對象是否處于被鎖定狀態(tài),撤銷偏向鎖后恢復到未鎖定(標志位為“01”)或輕量級鎖(標志位為“00”)的狀態(tài)。

3、重量級鎖、輕量級鎖和偏向鎖之間轉(zhuǎn)換

                                        圖 2.3三者的轉(zhuǎn)換圖

  該圖主要是對上述內(nèi)容的總結(jié),如果對上述內(nèi)容有較好的了解的話,該圖應該很容易看懂。

四、其他優(yōu)化

1、適應性自旋(Adaptive Spinning):從輕量級鎖獲取的流程中我們知道,當線程在獲取輕量級鎖的過程中執(zhí)行CAS操作失敗時,是要通過自旋來獲取重量級鎖的。問題在于,自旋是需要消耗CPU的,如果一直獲取不到鎖的話,那該線程就一直處在自旋狀態(tài),白白浪費CPU資源。解決這個問題最簡單的辦法就是指定自旋的次數(shù),例如讓其循環(huán)10次,如果還沒獲取到鎖就進入阻塞狀態(tài)。但是JDK采用了更聰明的方式——適應性自旋,簡單來說就是線程如果自旋成功了,則下次自旋的次數(shù)會更多,如果自旋失敗了,則自旋的次數(shù)就會減少。

2、鎖粗化(Lock Coarsening):鎖粗化的概念應該比較好理解,就是將多次連接在一起的加鎖、解鎖操作合并為一次,將多個連續(xù)的鎖擴展成一個范圍更大的鎖。舉個例子:

package com.paddx.test.string;

public class StringBufferTest {
  StringBuffer stringBuffer = new StringBuffer();

  public void append(){
    stringBuffer.append("a");
    stringBuffer.append("b");
    stringBuffer.append("c");
  }
}

  這里每次調(diào)用stringBuffer.append方法都需要加鎖和解鎖,如果虛擬機檢測到有一系列連串的對同一個對象加鎖和解鎖操作,就會將其合并成一次范圍更大的加鎖和解鎖操作,即在第一次append方法時進行加鎖,最后一次append方法結(jié)束后進行解鎖。

3、鎖消除(Lock Elimination):鎖消除即刪除不必要的加鎖操作。根據(jù)代碼逃逸技術,如果判斷到一段代碼中,堆上的數(shù)據(jù)不會逃逸出當前線程,那么可以認為這段代碼是線程安全的,不必要加鎖。看下面這段程序:

package com.paddx.test.concurrent;

public class SynchronizedTest02 {

  public static void main(String[] args) {
    SynchronizedTest02 test02 = new SynchronizedTest02();
    //啟動預熱
    for (int i = 0; i < 10000; i++) {
      i++;
    }
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
      test02.append("abc", "def");
    }
    System.out.println("Time=" + (System.currentTimeMillis() - start));
  }

  public void append(String str1, String str2) {
    StringBuffer sb = new StringBuffer();
    sb.append(str1).append(str2);
  }
}

雖然StringBuffer的append是一個同步方法,但是這段程序中的StringBuffer屬于一個局部變量,并且不會從該方法中逃逸出去,所以其實這過程是線程安全的,可以將鎖消除。下面是我本地執(zhí)行的結(jié)果:

  為了盡量減少其他因素的影響,這里禁用了偏向鎖(-XX:-UseBiasedLocking)。通過上面程序,可以看出消除鎖以后性能還是有比較大提升的。

  注:可能JDK各個版本之間執(zhí)行的結(jié)果不盡相同,我這里采用的JDK版本為1.6。

五、總結(jié)

  本文重點介紹了JDk中采用輕量級鎖和偏向鎖等對Synchronized的優(yōu)化,但是這兩種鎖也不是完全沒缺點的,比如競爭比較激烈的時候,不但無法提升效率,反而會降低效率,因為多了一個鎖升級的過程,這個時候就需要通過-XX:-UseBiasedLocking來禁用偏向鎖。下面是這幾種鎖的對比:

優(yōu)點

缺點

適用場景

偏向鎖

加鎖和解鎖不需要額外的消耗,和執(zhí)行非同步方法比僅存在納秒級的差距。

如果線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗。

適用于只有一個線程訪問同步塊場景。

輕量級鎖

競爭的線程不會阻塞,提高了程序的響應速度。

如果始終得不到鎖競爭的線程使用自旋會消耗CPU。

追求響應時間。

同步塊執(zhí)行速度非常快。

重量級鎖

線程競爭不使用自旋,不會消耗CPU。

線程阻塞,響應時間緩慢。

追求吞吐量。

同步塊執(zhí)行速度較長。


相關文章

  • SprinBoot整合Quart實現(xiàn)定時調(diào)度的示例代碼

    SprinBoot整合Quart實現(xiàn)定時調(diào)度的示例代碼

    這篇文章主要介紹了SprinBoot整合Quart實現(xiàn)定時調(diào)度的示例代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-10-10
  • Linux服務器如何部署java項目

    Linux服務器如何部署java項目

    這篇文章主要介紹了Linux服務器如何部署java項目問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Java中Object類常用的12個方法(小結(jié))

    Java中Object類常用的12個方法(小結(jié))

    Java 中的 Object 方法在面試中是一個非常高頻的點,本文主要介紹了Java中Object類常用的12個方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • 一文搞懂Java的ThreadPoolExecutor原理

    一文搞懂Java的ThreadPoolExecutor原理

    都說經(jīng)典的就是好的,這句話放在Java的ThreadPoolExecutor上那是一點都沒錯,像現(xiàn)在數(shù)據(jù)庫連接的池化實現(xiàn),或者像Tomcat這種WEB服務器的線程管理,處處都有著ThreadPoolExecutor的影子,本篇文章將結(jié)合源碼實現(xiàn),對ThreadPoolExecutor的原理進行一個深入學習
    2023-06-06
  • 最新評論

    闵行区| 通化市| 湛江市| 邳州市| 镇康县| 同江市| 永兴县| 合阳县| 东乡县| 白河县| 乌兰浩特市| 咸丰县| 新巴尔虎右旗| 新丰县| 息烽县| 贵南县| 仙居县| 老河口市| 新河县| 左权县| 土默特右旗| 青冈县| 菏泽市| 龙海市| 奉化市| 浠水县| 栾川县| 武穴市| 社旗县| 勃利县| 芦溪县| 寿光市| 营山县| 望城县| 仪征市| 乐清市| 台中市| 武定县| 泌阳县| 连南| 永吉县|