大家好,我是烤鸭:

前几天看马士兵老师的并发的课,里边讲到了 synchronize 锁的膨胀过程,今天想用代码演示一下。

1.  简单介绍

关于synchronize jdk 1.5 以后的优化,由重量级锁调整为膨胀过程。分别是 偏向锁 轻量级锁(自旋锁) 重量级锁。

2.  用例编写

pom文件增加 jol的包,用于看对象头的信息。

<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-core</artifactId><version>0.10</version>
</dependency>

下面的注释已经写的挺清楚的了,关于锁几种状态的转换。

SyncSourceTest.java

package src.source;import org.openjdk.jol.info.ClassLayout;/*** Created by test on 2020/5/10*/
public class SyncSourceTest {static Object noLock;static Object biaseLock;static Object lightLock;static Object heavyLock;public static void main(String[] args) throws InterruptedException {noLock = new Object();// 无锁状态,由于print 方法是synchronize 修饰,其实打印语句就已经是加偏向锁了(如果满足下面的偏向锁条件)System.out.print("线程["+Thread.currentThread().getName()+"]:无锁状态对象布局:"+ClassLayout.parseInstance(noLock).toPrintable());// 偏向锁,由于JVM 默认偏向锁4s后开启,可以线程sleep.5 或者设置VM参数关闭延迟 -XX:BiasedLockingStartupDelay=0Thread.sleep(5000L);biaseLock = new Object();System.out.println("线程["+Thread.currentThread().getName()+"]:偏向锁状态对象布局:"+ClassLayout.parseInstance(biaseLock).toPrintable());// 轻量级锁,由于轻量级锁是偏向锁升级的,需要先给对象一个偏向锁,如果不加偏向锁,只有一个线程加锁变成偏向锁lightLock = new Object();synchronized (lightLock) {System.out.println("线程["+Thread.currentThread().getName()+"]:[轻量级锁提前加偏向锁]轻量级锁状态对象布局:"+ClassLayout.parseInstance(lightLock).toPrintable());}synLight();// 重量级锁heavyLock = new Object();synHeavy();}public static void synLight() throws InterruptedException {for (int i = 0; i < 1; i++) {getLightLock();}}public static void getLightLock() {new Thread(() -> {try {synchronized (lightLock){System.out.println("线程["+Thread.currentThread().getName()+"]:轻量级锁状态对象布局:"+ClassLayout.parseInstance(lightLock).toPrintable());}} catch (Exception e) {e.printStackTrace();}}).start();}public static void synHeavy() throws InterruptedException {for (int i = 0; i < 2; i++) {getHeavyLock();}}private static void getHeavyLock() {new Thread(() -> {try {synchronized (heavyLock){System.out.println("线程["+Thread.currentThread().getName()+"]:重量级锁状态对象布局:"+ClassLayout.parseInstance(heavyLock).toPrintable());}} catch (Exception e) {e.printStackTrace();}}).start();}
}

关于对象布局,我们就先不研究了,这里重点说一下 对象头

HotSpot虚拟机的对象头(Object Header)分为两部分,第一 部分用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄(Generational GC Age) 等。这部分数据的长度在32位和64位的Java虚拟机中分别会占用32个或64个比特,官方称它为“Mark Word”。这部分是实现轻量级锁和偏向锁的关键。另外一部分用于存储指向方法区对象类型数据的指 针,如果是数组对象,还会有一个额外的部分用于存储数组长度。
运行之后我们看一下输出信息:
线程[main]:无锁状态对象布局:java.lang.Object object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
线程[main]:偏向锁状态对象布局:java.lang.Object object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           05 00 00 00 (00000101 00000000 00000000 00000000) (5)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total线程[main]:[轻量级锁提前加偏向锁]轻量级锁状态对象布局:java.lang.Object object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           05 28 e3 02 (00000101 00101000 11100011 00000010) (48441349)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total线程[Thread-2]:重量级锁状态对象布局:java.lang.Object object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           ba ee 0d 26 (10111010 11101110 00001101 00100110) (638447290)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total线程[Thread-0]:轻量级锁状态对象布局:java.lang.Object object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           88 f5 a4 29 (10001000 11110101 10100100 00101001) (698676616)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total线程[Thread-1]:重量级锁状态对象布局:java.lang.Object object internals:OFFSET  SIZE   TYPE DESCRIPTION                               VALUE0     4        (object header)                           ba ee 0d 26 (10111010 11101110 00001101 00100110) (638447290)4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalProcess finished with exit code 0

可以看出 无锁状态下各个状态下 mark word 后三位的值:

无锁:001

偏向:101

轻量级锁:000

重量级锁:010

本来想写一下,关于 轻量级锁 当前线程栈帧中 lock record 和 mark word的变化,无奈查了很多资料不知道在哪可以看到 lock record,有的说是显式或者隐式地创建lock record 空间,咱也不清楚了。更多关于轻量级锁的源码看这篇吧。

https://blog.csdn.net/z69183787/article/details/104502540?utm_source=app

3. 编译class文件

看下面的class文件,顺便说一下jvm的字节码指令。

可以看下 class文件里边对象的变化:

7: putstatic     #3                  // Field noLock:Ljava/lang/Object; 静态变量 初始化时

39: getstatic     #3                  // Field noLock:Ljava/lang/Object; 获取 静态变量

而到了偏向锁对象初始化之前,线程 睡眠了5秒。

57: ldc2_w        #16                   // long 5000l ,5000入栈
60: invokestatic  #18                 // Method java/lang/Thread.sleep:(J)V , 执行 sleep

135: monitorenter 对应的这行代码 :synchronized (lightLock)

184: monitorexit  加锁结束

190: monitorexit  后面又有一次 加锁结束

原因是 线程内部加锁后,调用 print 方法,又加了一次锁(重入锁),所以需要释放两次。

D:\gitee\rep\leetcode-gradle\src\main\java\src\source> javac -classpath ".;D:\dev\repository\org\openjdk\jol\jol-core\0.10\jol-core-0.10.jar"  -encoding UTF-8 .\SyncSourceTest.javaD:\gitee\rep\leetcode-gradle\src\main\java\src\source> javap -c .\SyncSourceTest.class
Compiled from "SyncSourceTest.java"
public class src.source.SyncSourceTest {static java.lang.Object noLock;static java.lang.Object biaseLock;static java.lang.Object lightLock;static java.lang.Object heavyLock;public src.source.SyncSourceTest();Code:0: aload_01: invokespecial #1                  // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]) throws java.lang.InterruptedException;Code:0: new           #2                  // class java/lang/Object3: dup4: invokespecial #1                  // Method java/lang/Object."<init>":()V7: putstatic     #3                  // Field noLock:Ljava/lang/Object;10: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;13: new           #5                  // class java/lang/StringBuilder16: dup17: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V20: ldc           #7                  // String 线程[22: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;25: invokestatic  #9                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;28: invokevirtual #10                 // Method java/lang/Thread.getName:()Ljava/lang/String;31: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;34: ldc           #11                 // String ]:无锁状态对象布局:36: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;39: getstatic     #3                  // Field noLock:Ljava/lang/Object;42: invokestatic  #12                 // Method org/openjdk/jol/info/ClassLayout.parseInstance:(Ljava/lang/Object;)Lorg/openjdk/jol/info/ClassLayout;45: invokevirtual #13                 // Method org/openjdk/jol/info/ClassLayout.toPrintable:()Ljava/lang/String;48: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;51: invokevirtual #14                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;54: invokevirtual #15                 // Method java/io/PrintStream.print:(Ljava/lang/String;)V57: ldc2_w        #16                 // long 5000l60: invokestatic  #18                 // Method java/lang/Thread.sleep:(J)V63: new           #2                  // class java/lang/Object66: dup67: invokespecial #1                  // Method java/lang/Object."<init>":()V70: putstatic     #19                 // Field biaseLock:Ljava/lang/Object;73: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;76: new           #5                  // class java/lang/StringBuilder79: dup80: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V83: ldc           #7                  // String 线程[85: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;88: invokestatic  #9                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;91: invokevirtual #10                 // Method java/lang/Thread.getName:()Ljava/lang/String;94: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;97: ldc           #20                 // String ]:偏向锁状态对象布局:99: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;102: getstatic     #19                 // Field biaseLock:Ljava/lang/Object;105: invokestatic  #12                 // Method org/openjdk/jol/info/ClassLayout.parseInstance:(Ljava/lang/Object;)Lorg/openjdk/jol/info/ClassLayout;108: invokevirtual #13                 // Method org/openjdk/jol/info/ClassLayout.toPrintable:()Ljava/lang/String;111: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;114: invokevirtual #14                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;117: invokevirtual #21                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V120: new           #2                  // class java/lang/Object123: dup124: invokespecial #1                  // Method java/lang/Object."<init>":()V127: putstatic     #22                 // Field lightLock:Ljava/lang/Object;130: getstatic     #22                 // Field lightLock:Ljava/lang/Object;133: dup134: astore_1135: monitorenter136: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;139: new           #5                  // class java/lang/StringBuilder142: dup143: invokespecial #6                  // Method java/lang/StringBuilder."<init>":()V146: ldc           #7                  // String 线程[148: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;151: invokestatic  #9                  // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;154: invokevirtual #10                 // Method java/lang/Thread.getName:()Ljava/lang/String;157: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;160: ldc           #23                 // String ]:[轻量级锁提前加偏向锁]轻量级锁状态对象布局:162: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;165: getstatic     #22                 // Field lightLock:Ljava/lang/Object;168: invokestatic  #12                 // Method org/openjdk/jol/info/ClassLayout.parseInstance:(Ljava/lang/Object;)Lorg/openjdk/jol/info/ClassLayout;171: invokevirtual #13                 // Method org/openjdk/jol/info/ClassLayout.toPrintable:()Ljava/lang/String;174: invokevirtual #8                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;177: invokevirtual #14                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;180: invokevirtual #21                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V183: aload_1184: monitorexit185: goto          193188: astore_2189: aload_1190: monitorexit191: aload_2192: athrow193: invokestatic  #24                 // Method synLight:()V196: new           #2                  // class java/lang/Object199: dup200: invokespecial #1                  // Method java/lang/Object."<init>":()V203: putstatic     #25                 // Field heavyLock:Ljava/lang/Object;206: invokestatic  #26                 // Method synHeavy:()V209: returnException table:from    to  target type136   185   188   any188   191   188   anypublic static void synLight() throws java.lang.InterruptedException;Code:0: iconst_01: istore_02: iload_03: iconst_14: if_icmpge     167: invokestatic  #27                 // Method getLightLock:()V10: iinc          0, 113: goto          216: returnpublic static void getLightLock();Code:0: new           #28                 // class java/lang/Thread3: dup4: invokedynamic #29,  0             // InvokeDynamic #0:run:()Ljava/lang/Runnable;9: invokespecial #30                 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V12: invokevirtual #31                 // Method java/lang/Thread.start:()V15: returnpublic static void synHeavy() throws java.lang.InterruptedException;Code:0: iconst_01: istore_02: iload_03: iconst_24: if_icmpge     167: invokestatic  #32                 // Method getHeavyLock:()V10: iinc          0, 113: goto          216: return
}

4.  总结

有很多文章对 synchronize 分析过,我这里只是想使用代码演示一下各种场景,很多地方并没有深入到源码和原理层面。

简单来说,就是:

无锁:mark word 记录 hashcode和分代年龄。

单线程加锁(偏向锁),mark word 记录线程id。

偏向锁升级到 轻量级锁,mark word 值 替换为 当前线程栈中的lock record 的指针。

轻量级锁到重量级锁:mark word 值 重量级锁 的指针。

其中自旋锁是 轻量级锁到重量级锁 发生的:

用CAS操作尝试把对象的Mark Word更新为指向Lock Record的指针,如果成功了,就是轻量级锁,失败了,就变成重量级锁。

从 class 文件 看 synchronize 锁膨胀过程(偏向锁 轻量级锁 自旋锁 重量级锁)相关推荐

  1. 12.synchronized的锁重入、锁消除、锁升级原理?无锁、偏向锁、轻量级锁、自旋、重量级锁

    小陈:呼叫老王...... 老王:来了来了,小陈你准备好了吗?今天我们来讲synchronized的锁重入.锁优化.和锁升级的原理 小陈:早就准备好了,我现在都等不及了 老王:那就好,那我们废话不多说 ...

  2. 带你彻底搞懂锁膨胀,偏向锁,轻量级锁,重量级锁

    1.synchronized 我们都知道synchronized内部有四种状态,分别是:无锁.偏向锁.轻量级锁和重量级锁,所以要搞懂这几种锁之间的变化我们得对synchronized有个大致的了解. ...

  3. (JUC)图文并茂!!!! 超详细 偏向锁VS轻量级锁VS重量级锁VS自旋

    偏向锁,轻量级锁,重量级锁 前言 java对象内存布局 (1)重量级锁(Moniter) (2)轻量级锁 锁膨胀.自旋.自适应自旋 (3)偏向锁(以64位虚拟机为例) 2.偏向锁撤销的3种情况 (1) ...

  4. synchronized的偏向锁、轻量级锁和重量级锁

    文章目录 Java对象头 偏向锁 批量重偏向 批量撤销 轻量级锁 重量级锁 Monitor 原理 Java对象头 普通对象 Mark Word在64 位虚拟机中的结构为 Mark Word后三(两)位 ...

  5. 十二、偏向锁、轻量级锁、重量级锁,锁的膨胀过程

    1.对象的状态有几种: 无锁.偏向锁.轻量级锁.重量级锁.GC标记,共5个状态 2.锁的膨胀过程 无锁:程序多线程执行过程中没有去执行synchronized修饰区域或者方法. 偏向锁:发生在程序单线 ...

  6. 锁升级过程(偏向锁/轻量级锁/重量级锁)

    锁的前置知识 如果想要透彻的理解java锁的来龙去脉,需要先了解锁的基础知识:锁的类型.java线程阻塞的代价.Markword. 锁的类型 锁从宏观上分类,分为悲观锁与乐观锁. 乐观锁 乐观锁是一种 ...

  7. Synchronize的底层优化(CAS,重量级锁,轻量级锁,偏向锁)

    Synchronize的底层优化(CAS,重量级锁,轻量级锁,偏向锁) Java 对象头 (重点) 对象头 对象头包含两部分: 运行时元数据(Mark Word)和类型指针(Klass Word) 运 ...

  8. Java如何避免重量级锁,Java 中锁是如何一步步膨胀的(偏向锁、轻量级锁、重量级锁)...

    文章目录 重量级锁(Mutex Lock) 偏向锁(比较 ThreadID) 偏向锁获取过程 偏向锁的释放 轻量级锁(自旋) 轻量级锁的加锁过程 轻量级锁的释放 总结 重量级锁(Mutex Lock) ...

  9. 谈谈JVM内部锁升级过程

    简介: 对象在内存中的内存布局是什么样的?如何描述synchronized和ReentrantLock的底层实现和重入的底层原理?为什么AQS底层是CAS+volatile?锁的四种状态和锁升级过程应 ...

最新文章

  1. javax.xml.ws.webserviceexception class do not have a property of the name
  2. Maven构建Struts2框架的注意事项
  3. Math工具类常用API使用案例
  4. jq中查找上级_云计算自动化运维之linux-工作中常用命令总结(上)
  5. 新手AS常见问题集锦
  6. [LeetCode]119.Pascal#39;s Triangle II
  7. 怎么看电脑电源多少w_UPS不间断电源设备哪个品牌好?如何选购家用电脑UPS电源?UPS电源价格多少?...
  8. 大数据之-Hadoop3.x_MapReduce_切片源码分析---大数据之hadoop3.x工作笔记0104
  9. 网易编程题-操作序列
  10. CS API 测试3
  11. 网络节点是计算机与网络的什么,网络节点是什么意思?
  12. java运行 .class文件_运行java的class文件方法详解
  13. 微型计算机原理实验二,微型计算机原理与接口技术实验指导(第2版)
  14. Java 分布式解决方案
  15. MBA-day16 数学-应用题-集合问题-公式
  16. 缠论中第49课:没必要参与操作级别及以上级别的下跌与超过操作级别的盘整,如何理解与应用?
  17. php20以内的勾股数,[求助]编程求100以内的所有勾股数
  18. ip解析经纬度,基站定位经纬度,用就完事了
  19. weblogic与oracle断开,菜鸟经验:oracle与weblogic自动启动与停止
  20. jsBlob数据转为图片

热门文章

  1. 前端学习(2925):vue改变样式1
  2. 工作287:命名报错
  3. 工作141:map形成数组
  4. 前端学习(1702):前端系列javascript之this
  5. 前端学习(1376):app.use方法
  6. 前端学习(808):复杂数据类型传参
  7. 前端学习(483):html之常用标签
  8. mybatis学习(20):模糊查询$
  9. 第一百五十三期: 云迁移可能失败的5种方式以及成功的5种方式
  10. 第一百三十四期:MySQL分页查询方法及优化