synchronized 底層原理

一、定義一個synchronized修飾的同步代碼塊 java

public class SyncTest {

   public int i;

   public void syncTask(){
       //同步代碼庫
       synchronized (this){
           i++;
       }
   }
}

二、編譯上述代碼並使用javap反編譯後獲得字節碼以下(這裏咱們省略一部分沒有必要的信息):git

Classfile /xxxx/SyncTest.class
  MD5 checksum c80bc322c87b312de760942820b4fed5
  Compiled from "SyncTest.java"
public class com.xx.xx.SyncTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
  //........省略常量池中數據
  //構造函數
  public com.xx.xx.SyncTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
  //===========主要看看syncTask方法實現================
  public void syncTask();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: dup
         2: astore_1
         3: monitorenter  //注意此處,進入同步方法
         4: aload_0
         5: dup
         6: getfield      #2             // Field i:I
         9: iconst_1
        10: iadd
        11: putfield      #2            // Field i:I
        14: aload_1
        15: monitorexit   //注意此處,退出同步方法
        16: goto          24
        19: astore_2
        20: aload_1
        21: monitorexit //注意此處,退出同步方法
        22: aload_2
        23: athrow
        24: return
      Exception table:
      //省略其餘字節碼.......
}
SourceFile: "SyncTest.java"

objectMonitor.cpp的源碼

監視器的實現

在Java虛擬機(HotSpot)中,Monitor是基於C++實現的,由ObjectMonitor實現的,其主要數據結構以下:github

ObjectMonitor() {
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL;
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

ObjectMonitor中有幾個關鍵屬性:數據結構

  • _owner:指向持有ObjectMonitor對象的線程函數

  • _WaitSet:存放處於wait狀態的線程隊列ui

  • _EntryList:存放處於等待鎖block狀態的線程隊列this

  • _recursions:鎖的重入次數spa

  • _count:用來記錄該線程獲取鎖的次數線程

當多個線程同時訪問一段同步代碼時,首先會進入_EntryList隊列中,當某個線程獲取到對象的monitor後進入_Owner區域並把monitor中的_owner變量設置爲當前線程,同時monitor中的計數器_count加1。即得到對象鎖。指針

若持有monitor的線程調用wait()方法,將釋放當前持有的monitor,_owner變量恢復爲null,_count自減1,同時該線程進入_WaitSet集合中等待被喚醒。若當前線程執行完畢也將釋放monitor(鎖)並復位變量的值,以便其餘線程進入獲取monitor(鎖)。以下圖所示

 

ObjectMonitor類中提供了幾個方法:

得到鎖

void ATTR ObjectMonitor::enter(TRAPS) {
  Thread * const Self = THREAD ;
  void * cur ;
//經過CAS嘗試把monitor的`_owner`字段設置爲當前線程
  cur = Atomic::cmpxchg_ptr (Self, &_owner, NULL) ;
//獲取鎖失敗
  if (cur == NULL) {         assert (_recursions == 0   , "invariant") ;
     assert (_owner      == Self, "invariant") ;
     // CONSIDER: set or assert OwnerIsThread == 1
     return ;
  }
//若是舊值和當前線程同樣,說明當前線程已經持有鎖,這次爲重入,_recursions自增,並得到鎖。
  if (cur == Self) { 
     // TODO-FIXME: check for integer overflow!  BUGID 6557169.
     _recursions ++ ;
     return ;
  }
//若是當前線程是第一次進入該monitor,設置_recursions爲1,_owner爲當前線程
  if (Self->is_lock_owned ((address)cur)) { 
    assert (_recursions == 0, "internal state error");
    _recursions = 1 ;
    // Commute owner from a thread-specific on-stack BasicLockObject address to
    // a full-fledged "Thread *".
    _owner = Self ;
    OwnerIsThread = 1 ;
    return ;
  }
//省略部分代碼。
//經過自旋執行ObjectMonitor::EnterI方法等待鎖的釋放
  for (;;) {
  jt->set_suspend_equivalent();
  // cleared by handle_special_suspend_equivalent_condition()
  // or java_suspend_self()
  EnterI (THREAD) ;
  if (!ExitSuspendEquivalent(jt)) break ;
  //
  // We have acquired the contended monitor, but while we were
  // waiting another thread suspended us. We don't want to enter
  // the monitor while suspended because that would surprise the
  // thread that suspended us.
  //
      _recursions = 0 ;
  _succ = NULL ;
  exit (Self) ;
  jt->java_suspend_self();
}
}

獲取鎖的執行流程以下圖所示:

釋放鎖

void ATTR ObjectMonitor::exit(TRAPS) {
   Thread * Self = THREAD ;
//若是當前線程不是Monitor的全部者
   if (THREAD != _owner) { 
     if (THREAD->is_lock_owned((address) _owner)) { // 
       // Transmute _owner from a BasicLock pointer to a Thread address.
       // We don't need to hold _mutex for this transition.
       // Non-null to Non-null is safe as long as all readers can
       // tolerate either flavor.
       assert (_recursions == 0, "invariant") ;
       _owner = THREAD ;
       _recursions = 0 ;
       OwnerIsThread = 1 ;
     } else {
       // NOTE: we need to handle unbalanced monitor enter/exit
       // in native code by throwing an exception.
       // TODO: Throw an IllegalMonitorStateException ?
       TEVENT (Exit - Throw IMSX) ;
       assert(false, "Non-balanced monitor enter/exit!");
       if (false) {
          THROW(vmSymbols::java_lang_IllegalMonitorStateException());
       }
       return;
     }
   }
//若是_recursions次數不爲0.自減
   if (_recursions != 0) {
     _recursions--;        // this is simple recursive enter
     TEVENT (Inflated exit - recursive) ;
     return ;
   }

 

省略部分代碼,根據不一樣的策略(由QMode指定),從cxq或EntryList中獲取頭節點,經過ObjectMonitor::ExitEpilog方法喚醒該節點封裝的線程,喚醒操做最終由unpark完成。

 

 

由此看來,monitor對象存在於每一個Java對象的對象頭中(存儲的指針的指向),synchronized鎖即是經過這種方式獲取鎖的,也是爲何Java中任意對象能夠做爲鎖的緣由,同時也是notify/notifyAll/wait等方法存在於頂級對象Object中的緣由,在使用這3個方法時,必須處於synchronized代碼塊或者synchronized方法中,不然就會拋出IllegalMonitorStateException異常,這是由於調用這幾個方法前必須拿到當前對象的監視器monitor對象,也就是說notify/notifyAll和wait方法依賴於monitor對象,在前面的分析中,咱們知道monitor 存在於對象頭的Mark Word 中(存儲monitor引用指針),而synchronized關鍵字能夠獲取 monitor ,這也就是爲何notify/notifyAll和wait方法必須在synchronized代碼塊或者synchronized方法調用的緣由。下面咱們將進一步分析synchronized在字節碼層面的具體語義實現。

相關文章
相關標籤/搜索