JUC学习笔记——共享模型之管程( 八 )


synchronized原理我们会从底层代码来讲解synchronized的原理:
/*源码*/static final Object lock = new Object();static int counter = 0;public static void main(String[] args) {synchronized (lock) {counter++;}}/*底层字节码*/public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=1// 下面是正式字节码过程// 正常运行阶段0: getstatic #2 // <- lock引用 (synchronized开始)3: dup4: astore_1 // lock引用 -> slot 1(这里提前存储一份锁对象,用于后续的解锁)5: monitorenter // 将 lock对象 MarkWord 置为 Monitor 指针(这里进行了上锁)6: getstatic #3 // <- i9: iconst_1 // 准备常数 110: iadd // +111: putstatic #3 // -> i14: aload_1 // <- lock引用15: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList(这里进行了解锁)16: goto 24// 报错补救阶段(去除锁)19: astore_2 // e -> slot 220: aload_1 // <- lock引用21: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList22: aload_2 // <- slot 2 (e)23: athrow // throw e// 代码结束24: return// 这里是异常检测器:当出现异常时,移动到异常处理底层代码区域进行解锁操作Exception table:from to target type61619any192219anyLineNumberTable:line 8: 0line 9: 6line 10: 14line 11: 24LocalVariableTable:Start Length Slot Name Signature0250args [Ljava/lang/String;StackMapTable: number_of_entries = 2frame_type = 255 /* full_frame */offset_delta = 19locals = [ class "[Ljava/lang/String;", class java/lang/Object ]stack = [ class java/lang/Throwable ]frame_type = 250 /* chop */offset_delta = 4轻量级锁我们首先来简单介绍一下轻量级锁:

  • 如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化 。
我们用一个简单的代码实现轻量级锁:
// 下面两个代码都调用了锁,但是他们归根结底属于一个流程,时间错开,这时系统就会避免直接使用Monitor而是用轻量级锁进行优化static final Object obj = new Object();public static void method1() {synchronized( obj ) {// 同步块 Amethod2();}}public static void method2() {synchronized( obj ) {// 同步块 B}}我们来展示一下实现流程(00轻量级锁,01无锁):
  1. 创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word

JUC学习笔记——共享模型之管程

文章插图
  1. 让锁记录中 Object reference 指向锁对象,并尝试用 cas 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录

JUC学习笔记——共享模型之管程

文章插图
  1. 如果 cas 替换成功,对象头中存储了 锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下

JUC学习笔记——共享模型之管程

文章插图
  1. 当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一

JUC学习笔记——共享模型之管程

文章插图
  1. 当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象头
除此之外我们需要注意cas切换不是每次都成功的:
  • 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程
  • 如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数
此外最后的cas恢复操作也不是都成功的: