总结:通过object获得内置锁(objectMonitor),调用内置锁的notify方法,通过_waitset结点移出等待链表中的首结点,将它置于_EntrySet中去,等待获取锁。注意:notifyAll根据policy不同可能移入_EntryList或者_cxq队列中,此处不详谈。
在了解对象头和ObjectMonitor后,接下来我们结合分析synchronzied的底层实现。
synchronzied的底层原理 synchronized修饰代码块通过下列简介的代码来分析:
public class test{ public void testSyn(){ synchronized(this){ } } }javac编译,javap -verbose反编译,结果如下:
/** * ... **/ public void testSyn(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter //申请获得对象的内置锁 4: aload_1 5: monitorexit //释放对象内置锁 6: goto 14 9: astore_2 10: aload_1 11: monitorexit //释放对象内置锁 12: aload_2 13: athrow 14: return此处我们只讨论了重量级锁(ObjectMonitor)的获取情况,其他锁的获取放在后面synchronzied的优化中进行说明。源码如下:
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") ; return ; } //如果之前的_owner指向该THREAD,那么该线程是重入,_recursions++ if (cur == Self) { _recursions ++ ; return ; } //如果当前线程是第一次进入该monitor,设置_recursions为1,_owner为当前线程 if (Self->is_lock_owned ((address)cur)) { assert (_recursions == 0, "internal state error"); _recursions = 1 ; //_recursions标记为1 _owner = Self ; //设置owner OwnerIsThread = 1 ; return ; } /** *此处省略锁的自旋优化等操作,统一放在后面synchronzied优化中说 **/总结:
如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的owner
如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权
synchronized修饰方法还是从简洁的代码来分析:
public class test{ public synchronized void testSyn(){ } }javac编译,javap -verbose反编译,结果如下:
/** * ... **/ public synchronized void testSyn(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=0, locals=1, args_size=1 0: return LineNumberTable: line 3: 0结果和synchronized修饰代码块的情况不同,仔细比较会发现多了ACC_SYNCHRONIZED这个标识,test.java通过javac编译形成的test.class文件,在该文件中包含了testSyn方法的方法表,其中ACC_SYNCHRONIZED标志位是1,当线程执行方法的时候会检查该标志位,如果为1,就自动的在该方法前后添加monitorenter和monitorexit指令,可以称为monitor指令的隐式调用。
上面所介绍的通过synchronzied实现同步用到了对象的内置锁(ObjectMonitor),而在ObjectMonitor的函数调用中会涉及到Mutex lock等特权指令,那么这个时候就存在操作系统用户态和核心态的转换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,这也是为什么早期的synchronized效率低的原因。在jdk1.6之后,从jvm层面做了很大的优化,下面主要介绍做了哪些优化。
synchronized的优化