一、定義一個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"
在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在字節碼層面的具體語義實現。