【问题标题】:How java object locking and monitor creation happens internally inside JVMjava对象锁定和监视器创建如何在JVM内部发生
【发布时间】: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 实现。 我期待一个低层次的解释。

我对这个主题研究了几天,但找不到深入的解释。这些是我经历的一些发现:

这些方法中最基本的是同步,它是使用监视器实现的。 Java 中的每个对象都与一个监视器相关联,线程可以锁定或解锁监视器。一次只有一个线程可以锁定监视器。任何其他试图锁定该监视器的线程都会被阻塞,直到它们能够获得对该监视器的锁定。一个线程 t 可能会多次锁定一个特定的监视器;每次解锁都会反转一次锁定操作的效果。

在 Java 虚拟机中,每个对象和类在逻辑上都与一个监视器相关联。对于对象,关联的监视器保护对象的实例变量。对于类,监视器保护类的类变量。如果一个对象没有实例变量,或者一个类没有类变量,则关联的监视器不会保护任何数据。

为了实现监视器的互斥功能,Java 虚拟机将锁(有时称为互斥锁)与每个对象和类相关联。锁就像在任何时候只有一个线程可以“拥有”的特权。线程不需要获得锁来访问实例或类变量。但是,如果一个线程确实获得了锁,那么在拥有锁的线程释放它之前,没有其他线程可以获得相同数据的锁。 (“锁定对象”就是获取与该对象关联的监视器。)

我知道在指令集级别如何使用 monitorentermonitorexit 操作码来管理同步语句。但我试图通过 JVM 源代码级别更深入地了解。但是我正在努力将 OpenJDK 源代码映射到我通过上面的链接找到的高级解释,因为源代码中有很多内容。

那么熟悉 OpenJDK 源码的人可以解释一下上面代码 sn-p 使用 OpenJDK 源码的相关问题吗?我认为ObjectMonitorBasicLockSynchronizer 类与这个解释更相关。

  1. 为哪个对象实例创建了监控对象?是用于MyWorker 对象实例还是Object lock1 或两者兼而有之?因为 JSL 和 Bill Vennams 的解释描述了每个对象都与一个监视器相关联。
  2. 如果是MyWorker 对象实例,如何为MyWorker 对象实例创建监视器?
  3. 如何为引用对象Object lock1创建锁对象 我们通过了
  4. 实际上监视器是如何被线程的锁定对象锁定的?

【问题讨论】:

  • 我不知道,但我认为锁定本身是通过测试和设置完成的:en.wikipedia.org/wiki/Test-and-set 这些是流行的机器指令,我相信英特尔会实施。如果测试和设置失败,那么我认为对所有者进行了检查,如果它是当前线程,则监视器锁仍然成功。否则线程被阻塞(或者在某些情况下会旋转等待)。
  • 为什么这个问题用 C++ 标记?
  • @SergeyA 因为我期待通过 C++ 中的 OpenJDK vm 源代码进行解释
  • 我认为问题并不清楚。您可能想特别要求指出 JDK 中的 C++ 代码并解释该代码。
  • 你不明白这一点。我已经明确指出这是关于与我的参考 java 代码相关的 OpenJDK 源代码的低级解释,如果你看得清楚的话。这就是为什么我从 OpenJDK 和 IMO C++ 标签中引用源代码文件的原因。

标签: java multithreading concurrency jvm-hotspot


【解决方案1】:

为哪个对象实例创建了监控对象?

每个Java对象也是一个监控对象,包括反射对象,所以你的代码至少有以下几点:

  • MyWorker 的类对象
  • Random 的类对象
  • Object 的类对象
  • List 的类对象
  • Integer 的类对象
  • ArrayList 的类对象
  • ...还有更多...
  • Random 分配给字段 random 的实例
  • Object 分配给字段 lock1 的实例
  • Object 分配给字段 lock2 的实例
  • ArrayList 分配给字段 list1 的实例
  • ArrayList 分配给字段 list2 的实例
  • Thread 实例分配给局部变量 t1
  • Thread 实例分配给局部变量 t2
  • 调用add(random.nextInt(100)) 时自动装箱创建的每个Integer 实例

是用于 MyWorker 对象实例还是对象 lock1 或两者?

两者

如果是 MyWorker 对象实例,如何为 MyWorker 对象实例创建监视器?
如何为我们传递的引用对象对象lock1创建锁对象
线程的锁对象实际上是如何锁定监视器的?

取决于 JVM 内部。对此没有单一的答案,“彻底的解释”超出了本网站的范围。

【讨论】:

  • 你能给我解释一下 HotSpot JVM 的实现吗?
  • @Andreas 你的直接沟通方式很吸引我。
  • @Insightcoder 解释什么具体?对不起,我不知道你在寻找什么“如何”。 --- 监视器如何在 CPU 级别下工作?这是一个与 Java 毫无关系的通用低级问题。
  • 是的,这正是一个与 Java 毫无关系的通用低级问题。这就是为什么我在问题中提到“我的问题不是关于此代码中的错误或它是如何执行的”。我只将 Java 代码作为参考,以便可以对此进行解释。我特意请教了一位有 OpenJDK 源代码经验的人的帮助。但似乎所有支持“否”答案的人甚至不理解这个问题都是关于特定 java 代码的低级解释。
  • @Insightcoder 既然你一次问了十几个问题,你不应该责怪回答者解决了 90% 的问题,但却错过了你的实际问题。如果您的实际问题是 JVM 如何在最低级别实现锁定,那么 90% 的问题文本都是过时的噪音。
猜你喜欢
  • 2011-07-03
  • 1970-01-01
  • 1970-01-01
  • 2014-05-02
  • 1970-01-01
  • 2014-08-22
  • 1970-01-01
相关资源
最近更新 更多