一.用法
就记住一点:synchronized修饰非静态方法时,锁是this,即当前的实例对象。synchronized修饰静态方法时,锁是类对象。
二.原理
2.1 预备知识(对象头)
- 在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。
- 对象头又分为Mark Word 和 Klass Word。Mark Word用于存储对象自身的运行时数据,如:哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID等。Klass Word为类型指针,Java虚拟机通过这个指针来确定该对象是哪个类的实例。如图1所示:
图1
2.2 Monitor
- Monitor被翻译为监视器或管程,是操作系统提供的对象。每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象A上锁(重量级锁)之后,该对象A的对象头的Mark Word中的相应部分就被设置指向Monitor对象的指针,Mark Word中的锁标志位置为10(即重量级锁)。如图2所示:
图2 - Monitor中分三个结构:Owner、EntryList、WaitSet。
(1)Owner:当某一线程Thread-2执行synchronized(obj)并成功加锁,该obj关联的Monitor的Owner就置为Thread-2。
(2)EntryList:用于存放那些等待Thread-2释放obj的线程,这些线程处于BLOCKED状态。
(3)WaitSet:用于存放那些之前获得过锁,但条件不满足而进入WAITING状态的线程。具体见后面的wait-notify。
具体见图3:
图3
2.3 Monitor-字节码角度
代码如图4所示:
图4
该段代码对应的字节码指令如图5所示:
图5
从图5中看出,15行和21行都是monitorexit指令,但为什么会有两次monitorexit指令呢?因为当顺利执行完16行时,锁已经释放了,就直接跳到24行,方法就正常返回了。但如果6到16行发生了异常,锁有可能没释放,就需要跳到19行(见图5异常表),然后往下执行,直到21行正确执行,这样能够保证锁顺利释放。