多线程教程(七)monitor概念
一、Java 对象头
java对象通常在内存中主要由两部分组成,一是对象头,另一部分是对象中的成员变量。
以 32 位虚拟机为例,对象头如下所示:
(1)普通对象
|--------------------------------------------------------------|
| Object Header (64 bits) |
|------------------------------------|-------------------------|
| Mark Word (32 bits) | Klass Word (32 bits) |
|------------------------------------|-------------------------|
klass word 部分表示类的类型,它是个指针,指向对象对应的class
(2)数组对象
|-------------------------------------------------------------------------|
| Object Header (96 bits) |
|-----------------------|------------------------|------------------------|
| Mark Word(32bits) | Klass Word(32bits) | array length(32bits) |
|-----------------------|------------------------|------------------------|
(3)其中 Mark Word 结构为
|----------------------------------------------------|--------------------|
| Mark Word (32 bits) | State |
|----------------------------------------------------|--------------------|
| hashcode:25 | age:4 | biased_lock:0 | 01 | Normal |
|----------------------------------------------------|--------------------|
| thread:23 | epoch:2 | age:4 | biased_lock:1 | 01 | Biased |
|----------------------------------------------------|--------------------|
| ptr_to_lock_record:30 | 00 | Lightweight Locked |
|----------------------------------------------------|--------------------|
| ptr_to_heavyweight_monitor:30 | 10 | Heavyweight Locked |
|----------------------------------------------------|--------------------|
| | 11 | Marked for GC |
|----------------------------------------------------|--------------------|
64 位虚拟机 Mark Word
|-------------------------------------------------------|-----------------|
| Mark Word (64 bits) | State |
|-------------------------------------------------------|-----------------|
|unused:25|hashcode:31|unused:1|age:4|biased_lock:0| 01 | Normal |
|-------------------------------------------------------|-----------------|
| thread:54 | epoch:2 |unused:1|age:4|biased_lock:1| 01 | Biased |
|-------------------------------------------------------|-----------------|
| ptr_to_lock_record:62 | 00 |LightweightLocked|
|-------------------------------------------------------|-----------------|
| ptr_to_heavyweight_monitor:62 | 10 |HeavyweightLocked|
|-------------------------------------------------------|-----------------|
| | 11 | Marked for GC |
|-------------------------------------------------------|-----------------|
二、monitor原理
Monitor 被翻译为监视器或管程
每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针
偏向锁和轻量级锁是不会关联Monitor的,具体原理后续章节(可能放在锁升级章节)会进行介绍
Monitor 结构如下
刚开始 Monitor 中 Owner 为 null
-
当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个 Owner
-
在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入EntryList,状态变为 BLOCKED
-
Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争的时是非公平的
-
图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,后面讲wait-notify 时会分析
简单点说waitSet里面就是拿到过这个锁的线程执行wait后会释放锁,进入waitset,等待后续拿到这个锁的线程执行notify或notifyall来激活在waitSet里的线程。
该方法常用来等待指定线程产生结果后继续执行。
注意:
synchronized 必须是进入同一个对象的 monitor 才有上述的效果
不加 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;)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 不会在字节码指令中有所体现