synchronized锁原理分析(二、锁的膨胀过程)锁原理分析(二、锁的膨胀过程)
继续承接上一篇博客一、从Java对象头看synchronized锁的状态
先通过几个案例,从结果直观的展示锁是如何膨胀的先通过几个案例,从结果直观的展示锁是如何膨胀的
最后将锁的膨胀过程通过一张流程图展示出来最后将锁的膨胀过程通过一张流程图展示出来
案例案例1(无锁,不可偏向状态)(无锁,不可偏向状态)
import org.openjdk.jol.info.ClassLayout;
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
A a = new A();
/// 理论上说这里应该是无锁状态理论上说这里应该是无锁状态
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
运行结果:
mark_word 最后3bit 是 001 ,表示 无锁,且不可偏向
案例案例2((HashCode存哪里)存哪里)
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
A a = new A();
System.out.println("a对象的hashcode:16进制:"+Integer.toHexString(a.hashCode()));
System.out.println("a对象的hashcode:2进制:"+Integer.toBinaryString(a.hashCode()));
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
运行结果:
可以看出: mark_word 最后3bit 是 001 ,表示 无锁,且不可偏向。这时候一旦进行对象的hashcode计算,那么hashcode就会存储在mark_word 的第26–56的位置,共占 31个bit,前
面25bit为0,不存任何信息。
由此可见,对象的hashcode是延迟加载的。
案例案例3(无锁,可偏向状态)(无锁,可偏向状态)
public class Demo1 {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(5000);
A a = new A();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
运行结果:
从运行结果可以看出:mark_word 最后3bit 是 101,而且前面54bit为0,说明a是一个无锁且可偏向状态。
为什么sleep了5秒钟,a对象的状态就和之前的不一样了呢?从一个不可偏向的无锁变成了一个可偏向的无锁!
JVM的偏向锁其实是可以进行开启和关闭的,偏向锁虽然是默认启用的,但是它在应用程序启动几秒钟之后才激活但是它在应用程序启动几秒钟之后才激活
这就解释了,为什么main方法中如果sleep一段时间,new出来的a对象,是一个无锁的可偏向状态,因为这个时候jvm已经激活了偏向锁机制。
我们可以通过使用JVM参数来关闭延迟 -XX:BiasedLockingStartupDelay=0,这样一来,程序在启动的时候new出来的对象就是可偏向状态的无锁对象。
当然还可以JVM参数关闭偏向锁-XX:-UseBiasedLocking
通过JVM的 参数 -XX:+PrintFlagsFinal可以查看出 偏向锁的启动延迟是4000微妙也就是4秒钟。