一、定义一个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在字节码层面的具体语义实现。