Synchronized的原理與鎖優化

前言

併發變成中,synchronized一直都是元老級角色,它一直被稱爲重量級鎖,在1.6中對synchronized進行各種優化過後才改善了它的性能消耗。爲了降低synchronized的性能消耗引入了偏向鎖,輕量級鎖,以及JVM底層對鎖存儲結構的更改。

synchronized的使用

java中一切皆對象,每一個對象都可以作爲鎖,這是synchronized實現的基礎,一般都隱氏的指定了鎖的對象,所以平時使用時並沒有指定參數,但是也可以顯示的指定。

synchronized的使用如下:

  • 普通同步方法:鎖是當前的實例對象,進入同步方法需要獲取實例對象鎖
  • 靜態同步方法:鎖是Class對象,需要獲取Class對象鎖才能訪問
  • 同步方法塊:鎖是括號裏的對象
   public void test(){
       Integer a =new Integer(1);
        //顯示指定鎖的對象
       synchronized (a){
           
       }
   }
    //靜態方法,鎖Class對象
   public synchronized static void get(){
       
   }
    //實例方法,鎖實例對象
   public synchronized void set(){
       
   }

synchronized原理

synchronized在JVM層的實現是基於monitor的進入與退出的,monitor可以翻譯成監視器或者管程,不過管程模型大家更加的熟悉,代碼塊的同步基於monitorenter和monitorexit指令,指令在編譯後插入到相應的位置,這對指令必須成對存在。任何的對象都有一個對應的monitor對象,monitor被持有後,處於鎖定狀態。線程執行指令會嘗試獲取monitor的所有權,也就是獲取對象的鎖。

各位瞭解了對象的內存佈局就知道對象頭中存儲着鎖相關的信息,持有不同的鎖,對象頭的mark word內容不同,不過我一直有個疑問,monitor對象是用c++寫的,monitor是存在哪裏的呢?留着這個問題,繼續往下說,JDK1.6前,synchronized是不推薦使用的,被稱爲重量級鎖,1.6進行了各種優化,引入了級別從低到高的偏向鎖、輕量級鎖、重量級鎖

偏向鎖

大多數情況下,鎖不僅不存在多線程競爭,而且總是同一線程多次獲取,爲了降低獲取鎖的代價,引入偏向鎖。這裏說下CAS,compareandswap,是一個原子操作,用來更新值,舉個例子:如果 int a = 1,多個線程需要更改a的值,使用CAS能夠解決併發問題主要包含以下操作,方法內,獲取a的值賦值給c,即將要賦予a的值爲b,先比較c與a是否相等,相等則a = b,否則其他線程改了a的值,重新進行cas操作。

線程訪問同步塊並獲取鎖時,在對象頭和棧楨鎖記錄中保存鎖偏向的線程ID,當本線程再次獲取鎖時,就不需要CAS的加鎖和解鎖,看一下mark word是否有指向當前線程的偏向鎖。如果沒有,看下mark word中是否是偏向鎖,如果是使用CAS修改偏向鎖修改爲當前線程,如果不是偏向鎖,使用CAS競爭鎖,下圖是流程:

輕量級鎖

線程執行同步代碼塊之前,JVM將當前線程的棧楨中創建存儲鎖記錄的空間,將對象頭的mark word複製到鎖記錄,然後線程通過CAS將mark word替換爲指向鎖記錄的指針,成功則獲取到鎖,失敗自旋獲取。流程圖如下:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章