Java对象锁定和监视器创建如何在JVM内部进行

时间:2018-06-20 20:47:23

标签: java multithreading concurrency jvm-hotspot

假设我有以下代码片段,其中两个线程使用两个关键部分(同步语句)访问同一方法。这些同步语句中的每一个都被赋予一个不同的锁对象。代码如下:

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虚拟机将锁(有时称为互斥锁)与每个对象和类关联。锁就像特权,一次只能有一个线程“拥有”。线程无需获取锁即可访问实例或类变量。但是,如果某个线程确实获得了锁,则只有拥有该锁的线程将其释放后,其他线程才能在同一数据上获得锁。 (“锁定对象”是获取与该对象关联的监视器。)

我知道在指令集级别如何使用 monitorenter monitorexit 操作码来管理同步语句。但是我试图通过JVM源代码级别更深入地了解。但是,由于我在源代码中有很多不足之处,因此我仍在努力将OpenJDK源代码与通过上述链接找到的高级解释进行映射。

任何熟悉OpenJDK源代码的人都能为使用OpenJDK源代码的上述代码段的以下问题提供解释吗?我认为ObjectMonitorBasicLockSynchronizer类与该解释更为相关。

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

1 个答案:

答案 0 :(得分:5)

  

为哪个对象实例创建监视对象?

每个 Java对象也是一个 monitor对象,包括反射对象,因此您的代码至少具有以下内容:

  • INSERT INTO table (a, b) VALUES (X, Y) ON DUPLICATE KEY UPDATE c = CASE WHEN c IS NULL THEN Y ELSE c END, d = CASE WHEN d IS NULL AND c IS NOT NULL THEN Y ELSE d END, e = CASE WHEN e IS NULL AND d IS NOT NULL THEN Y ELSE e END, f = CASE WHEN f IS NULL AND e IS NOT NULL THEN Y ELSE f END, g = CASE WHEN g IS NULL AND f IS NOT NULL THEN Y ELSE g END; 的类对象
  • var jObj= JObject.Parse(jsonstring); var result = jObj.SelectToken("whitelist").Children().OfType<JProperty>() .ToDictionary(p => p.Name, p=> new { Created = (DateTime)p.First()["create date"], Name = (string)p.First()["name"] }); 的类对象
  • __global__ void transpose_tiled_padded(float *A, float *B, int n) { int i_in = blockDim.x*blockIdx.x + threadIdx.x; int j_in = blockDim.y*blockIdx.y + threadIdx.y; int i_out = blockDim.x*blockIdx.y + threadIdx.x; int j_out = blockDim.y*blockIdx.x + threadIdx.y; extern __shared__ float tile[]; // coalesced read of A rows to (padded) shared tile column (transpose) tile[threadIdx.y + threadIdx.x*(blockDim.y+1)] = A[i_in + j_in*n]; __syncthreads(); // coalesced write from (padded) shared tile column to B rows B[i_out + j_out*n] = tile[threadIdx.x + threadIdx.y*(blockDim.x+1)]; } 的类对象
  • MyWorker的类对象
  • Random的类对象
  • Object的类对象
  • ...还有更多...
  • List实例已分配给字段Integer
  • ArrayList实例已分配给字段Random
  • random实例已分配给字段Object
  • lock1实例已分配给字段Object
  • lock2实例已分配给字段ArrayList
  • list1实例已分配给局部变量ArrayList
  • list2实例已分配给局部变量Thread
  • 调用t1时通过自动装箱创建的每个Thread实例
  

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

两者

  

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

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