java中的锁大家很快就能想到synchronized和lock,那么synchronized实现原理是怎样呢?本文将深入讲解synchronized实现原理。
synchronized关键字保证方法或者代码块在运行时,同一时刻只有一个方法可以进入临界区,同时它还可以保证共享变量的内存可见性。
synchronized 可以作用于方法和代码快,具体如下图:
作用于方法和代码块的实现原理是不同的,代码块采用的是monitorenter、monitorexit指令。而方法采用的是ACC_SYNCHRONIZED指令。
代码解读复制代码 public synchronized void test1(){
synchronized (this)
{
System.out.println("this is test method");
}
}
说明:从编译后的源码可以得知,代码块的同步jvm的实现采用的是关键字monitorenter、monitorexit。每个对象都与一个monitor相关联。当且仅当拥有所有者时(被拥有),monitor才会被锁定。执行到monitorenter指令的线程,会尝试去获得对应的monitor。
说明:加锁采用是monitorenter指令,每个对象维护着一个记录着被锁次数的计数器, 对象未被锁定时,该计数器为0。线程进入monitor(执行monitorenter指令)时,会把计数器设置为1.当同一个线程再次获得该对象的锁的时候,计数器再次自增.当其他线程想获得该monitor的时候,就会阻塞,直到计数器为0才能成功。
说明:释放锁采用的是monitorexi指令,线程执行monitorexit指令,就会让monitor的计数器减一。如果计数器为0,表明该线程不再拥有monitor。其他线程就允许尝试去获得该monitor。
说明:当调用一个设置了ACC_SYNCHRONIZED标志的方法,执行线程需要先获得monitor锁,然后开始执行方法,方法执行之后再释放monitor锁,当方法不管是正常return还是抛出异常都会释放对应的monitor锁。实现的流程图如下:
不管是作用于方法还是同步块,其加锁的原理都是通过monitor来实现的,那么monitor如何实现呢?
关于Java Monitor的工作机理如图所示:
虽然monitor我们已经知道了相关的实现原理,但是monitor与对象又是如何进行关联呢?
对象与monitor的关联其原理图如下:
在虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头、实例数据和对象填充。
而对象头又分为:Mark Word、类型指针、数组长度。
Mark Word 用于存储对象自身的运行时数据,包含哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等。
说明:当锁其膨胀成重量级锁后,其他竞争的线程进来就不会自旋了,而是直接阻塞等待,并且 Mark Word 中的内容会变成一个监视器(monitor)对象,用来统一管理排队的线程。而monitor对象与每个对象都会关联。monitor对象本质上是一个同步机制,保证了同时只有一个线程能够进入临界区,在 HotSpot的虚拟机中,是由C++类ObjectMonitor实现的。
本文对于synchronized的原理进行详细的讲解,关于synchronized锁的升级将在后续的文章进行讲解。如有疑问请随时反馈。