【发布时间】:2018-06-20 20:47:23
【问题描述】:
假设我有以下代码 sn-p,两个线程使用两个关键部分(同步语句)访问相同的方法。这些同步语句中的每一个都被赋予了一个不同的锁对象。代码如下:
public class MyWorker {
private Random random = new Random();
private Object lock1 = new Object();
private Object lock2 = new Object();
private List<Integer> list1 = new ArrayList<>();
private List<Integer> list2 = new ArrayList<>();
private void stageOne() {
synchronized (lock1) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
list1.add(random.nextInt(100));
}
}
private void stageTwo() {
synchronized (lock2) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
list2.add(random.nextInt(100));
}
}
private void process() {
for (int i=0; i<1000; i++) {
stageOne();
stageTwo();
}
}
void main() {
Thread t1 = new Thread(this::process);
Thread t2 = new Thread(this::process);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我的问题不是关于这段代码中的错误或者它在 java 的立场上是如何执行的。这段代码工作正常。我只是将此作为参考代码,以便回答的人有特定的场景可以参考 我想知道 JVM 如何在内部创建与此实例关联的监控对象以及对象锁定如何根据此场景在内部发生使用 OpenJDK 实现。 我期待一个低层次的解释。
我对这个主题研究了几天,但找不到深入的解释。这些是我经历的一些发现:
- 这个stackoverflow问题 Java lock concept how internally works?。 但我在答案中找不到详尽的解释。
- JLS 17.1 提供有关监视器如何工作的语言解释 高水平,但不是“内部发生的”。
这些方法中最基本的是同步,它是使用监视器实现的。 Java 中的每个对象都与一个监视器相关联,线程可以锁定或解锁监视器。一次只有一个线程可以锁定监视器。任何其他试图锁定该监视器的线程都会被阻塞,直到它们能够获得对该监视器的锁定。一个线程 t 可能会多次锁定一个特定的监视器;每次解锁都会反转一次锁定操作的效果。
- 还有 Bill Venners 的 Inside Java Virtual Machine 第 20 章“线程同步”
在 Java 虚拟机中,每个对象和类在逻辑上都与一个监视器相关联。对于对象,关联的监视器保护对象的实例变量。对于类,监视器保护类的类变量。如果一个对象没有实例变量,或者一个类没有类变量,则关联的监视器不会保护任何数据。
为了实现监视器的互斥功能,Java 虚拟机将锁(有时称为互斥锁)与每个对象和类相关联。锁就像在任何时候只有一个线程可以“拥有”的特权。线程不需要获得锁来访问实例或类变量。但是,如果一个线程确实获得了锁,那么在拥有锁的线程释放它之前,没有其他线程可以获得相同数据的锁。 (“锁定对象”就是获取与该对象关联的监视器。)
- OpenJDK 虚拟机source code
我知道在指令集级别如何使用 monitorenter 和 monitorexit 操作码来管理同步语句。但我试图通过 JVM 源代码级别更深入地了解。但是我正在努力将 OpenJDK 源代码映射到我通过上面的链接找到的高级解释,因为源代码中有很多内容。
那么熟悉 OpenJDK 源码的人可以解释一下上面代码 sn-p 使用 OpenJDK 源码的相关问题吗?我认为ObjectMonitor、BasicLock、Synchronizer 类与这个解释更相关。
- 为哪个对象实例创建了监控对象?是用于
MyWorker对象实例还是Object lock1或两者兼而有之?因为 JSL 和 Bill Vennams 的解释描述了每个对象都与一个监视器相关联。 - 如果是
MyWorker对象实例,如何为MyWorker对象实例创建监视器? - 如何为引用对象
Object lock1创建锁对象 我们通过了 - 实际上监视器是如何被线程的锁定对象锁定的?
【问题讨论】:
-
我不知道,但我认为锁定本身是通过测试和设置完成的:en.wikipedia.org/wiki/Test-and-set 这些是流行的机器指令,我相信英特尔会实施。如果测试和设置失败,那么我认为对所有者进行了检查,如果它是当前线程,则监视器锁仍然成功。否则线程被阻塞(或者在某些情况下会旋转等待)。
-
为什么这个问题用 C++ 标记?
-
@SergeyA 因为我期待通过 C++ 中的 OpenJDK vm 源代码进行解释
-
我认为问题并不清楚。您可能想特别要求指出 JDK 中的 C++ 代码并解释该代码。
-
你不明白这一点。我已经明确指出这是关于与我的参考 java 代码相关的 OpenJDK 源代码的低级解释,如果你看得清楚的话。这就是为什么我从 OpenJDK 和 IMO C++ 标签中引用源代码文件的原因。
标签: java multithreading concurrency jvm-hotspot