synchronized關(guān)鍵字(jvm)實現(xiàn)方式和鎖優(yōu)化

java程序中我們可以使用synchronized關(guān)鍵字對程序加鎖,它可以保證方法或者代碼塊運(yùn)行時同一時刻只有一個方法可以進(jìn)入到臨界區(qū)域,同時它還可以保證共享變量的內(nèi)存可見性,synchronzied關(guān)鍵字可以聲明一個同步代碼塊,也可以用來修飾靜態(tài)方法或者實例方法。當(dāng)synchronized修飾在不同地方時,它也是在對不同對象進(jìn)行加鎖操作:
1.同步代碼塊:鎖是括號里面的對象
2.靜態(tài)方法:瑣是當(dāng)前類的class對象
3.實例方法:鎖是當(dāng)前實例對象

當(dāng)聲明synchronized代碼塊時,編譯而成的字節(jié)碼將會包含monitorenter和monitorexit兩個指令,這兩種指令都會消耗操作數(shù)棧上的一個引用類型的元素(代碼塊中小括號里面的對象)作為加鎖和解鎖的對象。

  public void foo(Object sync) {
    synchronized (sync) {
      sync.hashCode();
    }
  }
  // 上面的 Java 代碼將編譯為下面的字節(jié)碼
  public void foo(java.lang.Object);
    Code:
       0: aload_1
       1: dup
       2: astore_2
       3: monitorenter
       4: aload_1
       5: invokevirtual java/lang/Object.hashCode:()I
       8: pop
       9: aload_2
      10: monitorexit
      11: goto          19
      14: astore_3
      15: aload_2
      16: monitorexit
      17: aload_3
      18: athrow
      19: return
    Exception table:
       from    to  target type
           4    11    14   any
          14    17    14   any

從上面的字節(jié)碼中可以看到有一個monitorenter指令和多個monitorexit指令,這是因為要確保在任何情況下退出都會釋放掉鎖。關(guān)于monitorenter和monitorexit的作用,我們可以抽象的理解為每個鎖對象都擁有一個鎖的計數(shù)器和一個持有該鎖的線程指針。
當(dāng)執(zhí)行到monitorenter時如果鎖計數(shù)器個數(shù)為零則代表沒有其他線程鎖定,這時java虛擬機(jī)會將鎖對象的持有線程設(shè)置為當(dāng)前線程,并把計數(shù)器設(shè)置為1。當(dāng)鎖計數(shù)器不為零時判斷持有鎖對象的線程是不是當(dāng)前線程,如果是當(dāng)前線程則把計數(shù)器加1。(因為synchronized是可重入鎖)當(dāng)執(zhí)行到monitorexit時,jvm會將計數(shù)器個數(shù)減1。當(dāng)計數(shù)器等于零的時候代表鎖已經(jīng)釋放。

當(dāng)synchronized標(biāo)記方法時,在字節(jié)碼中flags包括ACC_SYNCHRONIZED。此標(biāo)記標(biāo)識進(jìn)入該方法時java虛擬機(jī)要進(jìn)行monitorenter操作,在退出時進(jìn)行(正常退出或者異常退出)monitorexit操作。


sync.PNG

二 、JAVA對象頭

在java虛擬機(jī)中,每個java對象都有一個對象頭(object header),由標(biāo)記字段(Mark Word)和類型指針(Klass Pointer)構(gòu)成。標(biāo)記字段用來存儲對象運(yùn)行時Java虛擬機(jī)有關(guān)該對象的運(yùn)行數(shù)據(jù),如哈希碼、GC信息、鎖狀態(tài)標(biāo)志、線程持有的鎖等等。類型指針是對象指向它的類元數(shù)據(jù)的指針,Java虛擬機(jī)通過該指針確定這個對象是什么類的實例。

三、JVM鎖優(yōu)化

重量級鎖

重量級鎖是java虛擬機(jī)中最基本的鎖的實現(xiàn)方式,在這種鎖狀態(tài)下,獲取鎖失敗的線程會進(jìn)入阻塞狀態(tài),當(dāng)目標(biāo)鎖被釋放時喚醒阻塞線程。
java線程中的阻塞和喚醒都是依靠操作系統(tǒng)來實現(xiàn)的,但是這種方式會有系統(tǒng)調(diào)用,需要從操作系統(tǒng)的用戶狀態(tài)切換到內(nèi)核狀態(tài),開銷很大。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容