java synchronized
一.Synchronized使用方法
1.同步静态方法
private static synchronized t1(){
}
2.同步方法
private synchronized t2(){
}
3.同步代码块
synchronized(object){
}
4.线程同步
synchronized(object){
object.wait();//等待,保证当前线程已经获取了该对象的锁,否则调用wait会报异常;调用该方法,会强制升级到重量级锁,因为wait方法需要ObjectMonitor对象
object.notify();//通知,保证当前线程已经获取了该对象的锁,否则调用wait会报异常,会强制升级到重量级锁,因为wait方法需要ObjectMonitor对象
}
二.synchronized锁机制
i. 相关数据结构
synchronized锁机制需要java对象参与,也需要对象对应的ObjectMonitor参与。
当锁升级到重锁时,就要生成一个ObjectMonitor对象来实现重锁;偏向锁和轻量级锁时,不需要生成对象对应的ObjectMonitor对象。
先看看java对象结构
java对象由如下几部分组成:
1. 对象头:Mark word和klasspointer两部分组成;如果是数组,还包括数组长度
2. 实例属性
3. 对齐填充
如下图所示:
注意:图中 Class Pointer 应该是“klass pointer”,应该是类的元数据,不是其在堆中的Class对象
其中Mark Word 对象示意图:
ii.获取锁的过程如下:
1.如果当前对象没有生成过hashCode,那可以申请偏向锁(注意:如果对象计算过hashCode,会缓存到MarkWord中,后续申请锁的话,无法申请偏向锁;因为偏向锁机制没有保存原来的MarkWord值,如果还让申请偏向锁会导致hashCode丢失,如果重新计算hashCode 可能会导致和上次计算的hashCode不一样了(有的对象的hashCode计算函数就是生成一个随机数),这违反了java对象生成之后hashCode不会改变这个原则),否则只能申请轻量级锁。
hashCode和偏向锁不能共存:
当一个对象已经计算过identity hash code,它就无法进入偏向锁状态;
当一个对象当前正处于偏向锁状态,并且需要计算其identity hash code的话,则它的偏向锁会被撤销,并且锁会膨胀为轻量级锁或者重量锁;
2.在栈中构建lock recode,把lock recode中的 object属性设置为当前对象地址,设置当前线程id到markword中,同时设置偏向锁标志为1,(注意,偏向锁没有改变MarkWord中的“分代年龄”字段)
3.如果申请锁时,已经被其他线程申请了偏向锁了,会导致锁升级
3.1 暂停所有线程,如果持锁线程还在使用锁(说明线程同时竞争锁),直接升级到重量级锁(跳到步骤4);如果持锁线程不用锁了,跳到3.2
3.2 撤销偏向锁,把MarkWord设置为无锁状态
3.3 为设置轻量级锁,先保存当前MarkWord到 lock recode中
3.4 设置当前lock recode地址到MarkWord中,设置锁标志为0,完成轻量级锁设置
3.5 唤醒所有线程,当前线程通过cas获取轻量级锁
3.6 如果当前线程获取失败,升级到重量级锁
4.锁升级到重量级锁
4.1 暂停所有线程
4.2 新建一个ObjectMonitor对象,把该对象地址保存到MarkWord中,修改锁标志为2
4.3 当前线程通过cas自旋获取锁,如果自旋一段时间还未获得锁,进入睡眠
4.4 当前线程进入ObjectMonitor对象的等待队列,并进入睡眠状态,等待唤醒
4.5 持锁线程进入ObjectMonitor对象的执行队列,执行完临界区代码之后,会发现自己持有的锁,已经变成重量级锁了,会在退出临界区之前,唤醒其他等锁的线程
iii.原理图
该图只是示意图,好像逻辑不大对
三.底层原理
synchronized底层调用c++代码,实际使用操作系统的mutex(互斥量)来实现锁机制和操作系统的condition(条件变量)来实现同步机制(就是等待wait,通知nodify)
四.锁可以降级
网上说锁不可以降级,是不对的;可以降级
降级的目的和过程:
因为BasicLocking的实现优先于重量级锁的使用,JVM会尝试在SWT的停顿中对处于“空闲(idle)”状态的重量级锁进行降级(deflate)。这个降级过程是如何实现的呢?我们知道在STW时,所有的Java线程都会暂停在“安全点(SafePoint)”,此时VMThread通过对所有Monitor的遍历,或者通过对所有依赖于MonitorInUseLists值的当前正在“使用”中的Monitor子序列进行遍历,从而得到哪些未被使用的“Monitor”作为降级对象。
可以降级的Monitor对象:
重量级锁的降级发生于STW阶段,降级对象就是那些仅仅能被VMThread访问而没有其他JavaThread访问的Monitor对象,删除这些Monitor对象,把对应对象的对象头的Mark恢复到重量级锁之前的状态。
相关代码在:ObjectSynchronizer::deflate_idle_monitors()
注:文中图片来自网络,如果有侵权,请联系我!!!