**synchronized 原理 **
对应的字节码为
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: getstatic #2 // <- lock引用 (synchronized开始) 3: dup 4: astore_1 // lock引用 -> slot 1 5: monitorenter // 将 lock对象 MarkWord 置为 Monitor 指针 6: getstatic #3 // <- i 9: iconst_1 // 准备常数 1 10: iadd // +1 11: putstatic #3 // -> i 14: aload_1 // <- lock引用 15: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList 16: goto 24 19: astore_2 // e -> slot 2 20: aload_1 // <- lock引用 21: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList 22: aload_2 // <- slot 2 (e) 23: athrow // throw e 24: return Exception table: from to target type 6 16 19 any 19 22 19 any LineNumberTable: line 8: 0 line 9: 6 line 10: 14 line 11: 24 LocalVariableTable: Start Length Slot Name Signature 0 25 0 args [Ljava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 19 locals = [ class "[Ljava/lang/String;", class java/lang/Object ] stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4方法级别的 synchronized 不会在字节码指令中有所体现
synchronized 原理进阶
轻量级锁
轻量级锁的使用场景:如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化。
轻量级锁对使用者是透明的,即语法仍然是 synchronized
假设有两个方法同步块,利用同一个对象加锁
static final Object obj = new Object(); public static void method1() { synchronized (obj) { // 同步块 A method2(); } } public static void method2() { synchronized (obj) { // 同步块 B } } 创建锁记录对象( Lock Record),每个线程的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的 Mark Word 让锁记录中 Object reference 指向锁对象,并尝试用 cas 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录 如果 cas 替换成功,对象头中存储了锁记录地址和状态 00,表示由该线程给对象加锁,这时图示如下 如果 cas 失败,有两种情况 如果是其它线程已经持有了该 object 的轻量级锁,这时表明有竞争,进入锁膨胀过程如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数 当退出 synchronized 代码块 ( 解锁时 )如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一当退出 synchronized 代码块 (解锁时)锁记录的值不为null,这时使用 cas 将 Mark Word 的值恢复给对象头
当退出 synchronized 代码块 (解锁时)锁记录的值不为null,这时使用 cas 将 Mark Word 的值恢复给对象头
成功,则解锁成功失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程