你好,这里是codetrend专栏“高并发编程基础”。
在Java并发编程中,Monitor(监视器)是一种同步机制,用于实现线程间的互斥访问和共享资源的同步。它是一种基本的并发控制原语,在Java中以对象的形式存在。
每个Java对象都有一个与之关联的Monitor,可以通过synchronized关键字来使用该Monitor。当一个线程获得了一个对象的Monitor锁时,它就可以进入临界区,执行对应的同步代码块或方法。其他线程必须等待该Monitor的锁释放后才能进入临界区。
Monitor提供了以下核心概念:
Monitor在Java中被广泛应用于同步代码块、同步方法、管程等场景,用于保护共享资源的访问,避免并发访问的竞态条件和数据不一致问题。通过使用Monitor,可以实现线程安全和正确的多线程编程。
Java对象头(Object Header)是Java虚拟机中用于管理对象的一部分元数据,存储在每个Java对象的内存中。它包含了一些重要的信息,用于支持对象的运行时特性和垃圾回收。
Java对象头通常占用一个机器字(Word)大小,具体大小取决于虚拟机的实现和运行环境。
Java对象头中包含的信息可以分为两类:
Mark Word(标记字段):Mark Word是对象头中最重要的字段,用于存储对象的运行时数据和锁相关的信息。它可以包含以下内容:
类型指针(Klass Pointer):类型指针指向对象的类元数据,用于确定对象的类型信息,包括方法表、字段表等。通过类型指针,虚拟机可以确定对象的具体类型,并进行动态分派。
Java对象头在内存中紧跟在对象的地址之后,它是实现Java虚拟机的关键组成部分。对象头的布局和具体内容可能因不同的虚拟机实现而有所差异,但它们都具有类似的作用,用于支持对象的运行时特性、锁机制和垃圾回收。
每个 Java 对象都可以关联一个 Monitor 对象,这是通过对象头中的 Mark Word 实现的。在对象头中,Mark Word 可以存储一些运行时数据和锁相关的信息,其中一个重要的信息就是指向 Monitor 对象的指针。当我们使用 synchronized 给对象上锁时,该对象的 Mark Word 就会被设置为指向 Monitor 对象的指针,这样就将对象与 Monitor 对象关联了起来。
这种关联方式是基于“重量级锁”的实现方式。在 Java 中,synchronized 关键字的实现有两种方式:轻量级锁和重量级锁。轻量级锁是一种基于对象头中的 CAS 操作实现的锁,用于提高并发性能。但是,当轻量级锁无法满足锁的需求时,就会升级为重量级锁。重量级锁是一种基于 Monitor 对象实现的锁,它可以保证多线程之间的互斥访问和同步执行。
在重量级锁的实现中,每个 Monitor 对象都与一个互斥锁相关联,用于控制对共享资源的访问。当一个线程尝试获得一个被锁定的对象时,它就会阻塞,并且该对象的 Mark Word 中的锁状态标志会被设置为“重量级锁”。此时,该对象就不再使用轻量级锁了,而是使用基于 Monitor 对象实现的重量级锁。同时,该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针,将对象与 Monitor 对象关联起来。
因此,Java 对象头中的 Mark Word 和 Monitor 对象是实现 Java 中锁机制的两个关键元素。它们共同保证了多线程之间的互斥访问和同步执行,确保了程序的正确性和安全性。在多线程编程中,需要充分理解对象头和 Monitor 对象的作用,合理地使用 synchronized 关键字,才能编写出高效、安全和正确的多线程程序。
示例代码如下:
package engineer.concurrent.battle.fcontact;
public class MonitorObjectTest {
static final Object lock = new Object();
static int counter = 0;
public static void main(String[] args) {
synchronized (lock) {
counter++;
System.out.println(counter);
}
}
}
通过 javap -c MonitorObjectTest
输出字节码如下:
public class engineer.concurrent.battle.fcontact.MonitorObjectTest {
static final java.lang.Object lock;
static int counter;
public engineer.concurrent.battle.fcontact.MonitorObjectTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field lock:Ljava/lang/Object;
3: dup
4: astore_1
5: monitorenter // 将 lock对象 MarkWord 置为 Monitor 指针
6: getstatic #13 // Field counter:I
9: iconst_1
10: iadd
11: putstatic #13 // Field counter:I
14: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream;
17: getstatic #13 // Field counter:I
20: invokevirtual #23 // Method java/io/PrintStream.println:(I)V
23: aload_1
24: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList
25: goto 33
28: astore_2
29: aload_1
30: monitorexit
31: aload_2
32: athrow
33: return
Exception table:
from to target type
6 25 28 any
28 31 28 any
static {};
Code:
0: new #2 // class java/lang/Object
3: dup
4: invokespecial #1 // Method java/lang/Object."":()V
7: putstatic #7 // Field lock:Ljava/lang/Object;
10: iconst_0
11: putstatic #13 // Field counter:I
14: return
}
通过字节码可以发现 锁开始会执行monitorenter,锁结束会执行monitorexit。
来自一线全栈程序员nine的探索与实践,持续迭代中。
欢迎关注公众号“雨林寻北”或添加个人卫星codetrend(备注技术)。