对象头
普通对象:
数组对象:
java中对象存储结构分为对象头(Header)、实例数据(Instance Date)和对齐填充(Padding)。
对象头存储着Mark Word和Klass Word,通过Klass Word,程序才知道这个对象是一个什么对象。
Mark Word占32位。结构如下,有五种不同的类型,重点记一下:
01偏向锁、00轻量级锁、10重量级锁
Monitor
每个java对象都可以关联一个Monitor对象,当用synchronized给对象上锁(重量级)之后,该对象头的MarkWord中就被设置指向Monitor对象的指针。
图中流程:
1、刚开始Monitor中Owner为null
2、Thread1执行时,将Monitor所有者Owner置为Thread2。
3、Thread1上锁时,若有其他线程执行synchronized,进入EntryList阻塞blocked。
4、Thread1执行完后,唤醒EntryList中的线程来竞争锁,竞争非公平。
5、Thread0之前获得过锁,但是不满足执行条件,进入等待Waiting状态。
注意:
1、synchronized 必须是进入同一个对象的 monitor 才有上述的效果!
2、不加 synchronized 的对象不会关联监视器,不遵从以上规则!
Synchronized原理
我们可以查看一下synchronized的字节码文件。
把如下代码反编译:
public class Test {static int cnt = 0;static final Object lock = new Object();public static void main(String[] args) {synchronized (lock){cnt++;}}
}
public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: (0x0009) ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=10: getstatic #2 // Field lock:Ljava/lang/Object;3: dup4: astore_15: monitorenter6: getstatic #3 // Field cnt:I9: iconst_110: iadd11: putstatic #3 // Field cnt:I14: aload_115: monitorexit16: goto 2419: astore_220: aload_121: monitorexit22: aload_223: athrow24: returnException table:from to target type6 16 19 any19 22 19 any
逐行分析:
0: getstatic #2 // 引用lock对象
3: dup
4: astore_1 // lock引用存到 slot1
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 -> slot2
20: aload_1 // lock引用
21: monitorexit //将lock对象MarkWord重置,唤醒EntryList
22: aload_2 // <- slot 2 (e)
23: athrow //throw e
24: return
异常和正常都会正常解锁,不会造成死锁。下面有一个Exception table,当发生异常,会跳转到to指定的位置,保证锁的正常解锁。