synchronized关键字的原理刨析

共 3983字,需浏览 8分钟

 ·

2020-07-28 17:29

点击上方蓝色字体,选择“标星公众号”

优质文章,第一时间送达


66套java从入门到精通实战课程分享


前言

关于synchronized原理解析,我们首先要分析一下对象都有哪些东西,对象头到底存储了什么,synchronzied关键字到底是如何进行锁膨胀的,在使用过程中同步方法块和同步代码块到底有什么区别,在回头看synchronized的使用。针对的时JDK1.6之后的版本的synchronized深度分析。


前期准备

为输出对象头导入jar


<dependency>
    <groupId>org.openjdk.jolgroupId>
    <artifactId>jol-coreartifactId>
    <version>0.11version>
dependency>


设置偏向锁的启动延迟


#关闭偏向锁(为什么要关闭偏向锁,有什么好处嘛,这个问题先不做回答)
-XX:-UseBiasedLocking
#设置偏向锁的一个启动延迟
-XX:BiasedLockingStartupDelay=0

对象

对象中有哪些部分

新建一个对象,进行main方法输出


public class A {
   int status=0;
   boolean flag=true;
}


public static void main(String[] args) {
    a=new A(); System.out.println(ClassLayout.parseInstance(a).toPrintable());
}


输出的一个对象信息(当前的操作系统是一个64位的,32位的是不一样的)


从这个上面看,我们可以分析出包含96bit(12byte*8)的对象头和两个属性(status,flag)字段属性。这些都属于对象数据,int 类型的数据我们知道,占用4个byte,1个boolean值得可以使用1个byte去表示,那么为什么后面还出现了7个byte,这是因为jvm在分配内存的时候只能是8的一个倍数。所以就出现了7个byte。


所以我们小小的总结一下。


一个对象中包含对象头,实列数据,对象填充.


对象头有哪些东西

通过文档查找来验证


jvm底层一个是基于C/C++来实现的,查看版本java -version 可以知道jvm的实现是HotSport来实现的jvm规范和标准的。通过openjdk的官方文档 [https://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html]查看对于对象头的一个定义。

说了一下包含两部分,mark word 和 klass pointer(这个对象对应的class类指针地址) 两部分。在open Jdk的源码中有一个markOop的文件中说明了在64bit的操作系统中,mark word 占用了64位的。所以我们得出一个结论。


那么此时我们知道了,对象头中包含了 这个对象对应的class类的类型,Gc age,hashcode , synchronized关键字的同步状态。


对象头再深入分析


这里的klass word 为什么是32bit呢,因为在jvm中如果开启了指针压缩(1.8默认开启的)就会对对象头进行压缩。所以看到是32bit,如果关闭就会看到是64bit。

关闭指针压缩


-XX:-UseCompressedOops

锁的一个状态分析

new 对象A


public static void main(String[] args) {
   a=new A();
   System.out.println(ClassLayout.parseInstance(a).toPrintable());
   a.hashCode();
   System.out.println(ClassLayout.parseInstance(a).toPrintable());
}

第一次打印这个对象A的情况(没有计算a的hashcode)无锁可偏向,就是没有线程来获取这把锁,也没有计算hashcode。

全部为0的就是没有HashCode 或者 线程ID 是空的。


第二次打印这个对象A的情况(计算了a的hashcode),无锁不可偏向,没有线程来获取这把锁,但是计算了hashcode。


并设置了HashCode 到mark word 中。

当我们的代码变成这样下面这样时,我们在看这个对象头的中mark word 的一个锁的状态。


public static void main(String[] args) {
   a=new A();
   System.out.println(ClassLayout.parseInstance(a).toPrintable());
   a.hashCode();
   System.out.println(ClassLayout.parseInstance(a).toPrintable());
   lock();

   new Thread(()->{
      lock();
   }).start();
}

public  static  void  lock(){
   synchronized (a){
      System.out.println("线程名称=="+Thread.currentThread().getName());
      System.out.println(ClassLayout.parseInstance(a).toPrintable());
   }
}


main线程来进行加锁操作,main线程第一次来加锁,直接变成了轻量级锁,这是为什么呢,不应该是偏向锁嘛。


这是因为:对这个对象A进行了一个hashcode计算,那么hashcode占据了56个bit,锁的一个状态就变成了无锁,不可偏向状态。没有办法进行偏向,第一个线程来获取锁的时候就会变成轻量级锁。


此时我们把计算hashcode注释掉


public static void main(String[] args) {
   a=new A();
   System.out.println(ClassLayout.parseInstance(a).toPrintable());
 
   lock();

   new Thread(()->{
      lock();
   }).start();
}

public  static  void  lock(){
   synchronized (a){
      System.out.println("线程名称=="+Thread.currentThread().getName());
      System.out.println(ClassLayout.parseInstance(a).toPrintable());
   }
}
————————————————
版权声明:本文为CSDN博主「只穿T恤的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_36114346/article/details/107488920


运行代码得到一个 main线程加锁之后,对象头中的锁的一个状态变成了无锁 可偏向,但是多了一个线程ID。

线程Thread-0运行的一个情况,此时线程Thread-0变成了轻量级锁,线程ID为thread-0,发生了一个锁的膨胀。


但是先需要撤销偏向锁,再把锁的状态变成轻量级锁,而轻量级锁是一个CAS操作。相对撤销偏向锁来说来消耗的性能要低的多。

当再执行一个线程Thread-1时,此时就变成了重量级锁。从轻量级锁变成了重量级锁。

到这里我们分析完了一个锁的状态,包括锁膨胀情况。


画图总结锁的一个膨胀

没有进行HashCode运算的流程


进行过HashCode运算的流程


基本使用

synchronzied加锁的对象为A,叫对象锁 ,修饰的是代码块,进入同步代码块之前要获取锁。


public  static  void  lock(){
   synchronized (a){
      System.out.println("线程名称=="+Thread.currentThread().getName());
      System.out.println(ClassLayout.parseInstance(a).toPrintable());
   }
}


synchronzied在静态方法上加锁,加锁的类型是这个方法对应的类锁。进入同步方法之前要获取锁。


public   static    synchronized  void  lock2(){
}


synchronzied在实列方法上加锁,加锁的类型是这个方法对应的对象实列锁。进入同步方法之前要当前对象实列锁。


public     synchronized  void  lock2(){
}


————————————————

版权声明:本文为CSDN博主「只穿T恤的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:

https://blog.csdn.net/weixin_36114346/article/details/107488920


     




感谢点赞支持下哈 


浏览 37
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报